/**
 * Created by QianQi on 2016/1/5.
 */
"use strict";
if(window._GLOBAL == undefined){ window._GLOBAL={};}// 用于存放全局变量
if (!Array.prototype.indexOf){
    /**
     * 从 from 位置开始，返回首次出现 elt 的索引位置，未找到返回 -1
     * @param {*} elt 要判断是否在数组中的对象
     * @param {int=} from 搜索起始索引，0 到 length-1，默认为 0
     * @returns {*}
     */
    Array.prototype.indexOf = function(elt , from){
        var len = this.length >>> 0;// 用于确保 this.length 是可运算的数值
        from = Number(from) || 0;
        if (from < 0 || from >= len) return -1;
        for (; from < len; from++){
            if (this[from] === elt) return from;
        }
        return -1;
    };
}
/**
 * 字符串去除首尾空格
 * @returns {string}
 */
String.prototype.trim=function() {
    return this.replace(/(^\s*)|(\s*$)/g,'');
};
/**
 * 日期格式化（原型扩展或重载）
 * @param {string} formatStr 格式模版
 *  - YYYY/yyyy/YY/yy 表示年份
 *  - MM/M 月份
 *  - W/w 星期
 *  - dd/DD/d/D 日期
 *  - hh/HH/h/H 时间
 *  - mm/m 分钟
 *  - ss/SS/s/S 秒
 * @return {string} 日期字符串
 */
Date.prototype.format = function(formatStr){
    var str = formatStr;
    var Week = ['日','一','二','三','四','五','六'];
    str=str.replace(/yyyy|YYYY/,''+this.getFullYear());
    str=str.replace(/yy|YY/,(this.getYear() % 100)>9?(this.getYear() % 100).toString():'0' + (this.getYear() % 100));
    str=str.replace(/MM/,(this.getMonth()+1)>9?(this.getMonth()+1).toString():'0' + (this.getMonth()+1));
    str=str.replace(/M/g,this.getMonth()+1);
    str=str.replace(/w|W/g,Week[this.getDay()]);
    str=str.replace(/dd|DD/,this.getDate()>9?this.getDate().toString():'0' + this.getDate());
    str=str.replace(/d|D/g,this.getDate());
    str=str.replace(/hh|HH/,this.getHours()>9?this.getHours().toString():'0' + this.getHours());
    str=str.replace(/h|H/g,this.getHours());
    str=str.replace(/mm/,this.getMinutes()>9?this.getMinutes().toString():'0' + this.getMinutes());
    str=str.replace(/m/g,this.getMinutes());
    str=str.replace(/ss|SS/,this.getSeconds()>9?this.getSeconds().toString():'0' + this.getSeconds());
    str=str.replace(/s|S/g,this.getSeconds());
    return str;
};
/**
 * 日期计算（原型扩展或重载）
 * @param {string} interval 日期计算的单位
 *  - y,Y 年
 *  - M 月
 *  - q,Q 季度
 *  - w,W 周
 *  - d,D 日
 *  - h,H 时
 *  - m 分
 *  - s,S 秒
 * @param {number} number 数量
 * @returns {Date} 计算后的日期对象
 */
Date.prototype.add = function(interval, number) {
    switch (interval) {
        case 'S':
        case 's':return new Date(this.getTime() + (1000 * number));
        case 'm':return new Date(this.getTime() + (60000 * number));
        case 'H':
        case 'h':return new Date(this.getTime() + (3600000 * number));
        case 'D':
        case 'd':return new Date(this.getTime() + (86400000 * number));
        case 'W':
        case 'w':return new Date(this.getTime() + ((86400000 * 7) * number));
        case 'Q':
        case 'q':return new Date(this.getFullYear(), (this.getMonth()) + number*3, this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds());
        case 'M':return new Date(this.getFullYear(), (this.getMonth()) + number, this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds());
        case 'Y':
        case 'y':return new Date((this.getFullYear() + number), this.getMonth(), this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds());
    }
};
/** jquery 扩展 **/
(function($){
    $.noop=function(){};
    // session 过期将页面转到提示页面
//    $(document).ajaxComplete(function(event, xhr, settings) {
//        if (xhr.getResponseHeader('session_status') == 'timeout') {
//            location.href = $.getBathPath() + 'timeout.jsp';// TODO session 过期提示页面，location.host 主机名:端口号
//        }
////        if(settings.url!='menu.json'){
////            location.href = $.getBathPath()+'index/sessionTimeout.html';// TODO session 过期提示页面，location.host 主机名:端口号
////        }
//    });
    // 获取当前页面路径中 http://主机名:端口/项目名/
    $.getBathPath=function(){
        var prjMatch=location.pathname.match(/^\/?([^\/]+\/)/);
        return 'http://'+location.host+'/'+(prjMatch ? prjMatch[1] : '');
    };
    $.browser = {
        versions: function() {
            var u = navigator.userAgent;
            return {//移动终端浏览器版本信息
                trident: u.indexOf('Trident') > -1, //IE内核
                presto: u.indexOf('Presto') > -1, //opera内核
                webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
                gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
                mobile: !!u.match(/applewebkit.*mobile.*/) || !!u.match(/applewebkit/), //是否为移动终端
                ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
                android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
                iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者QQHD浏览器
                iPad: u.indexOf('iPad') > -1, //是否iPad
                webApp: u.indexOf('Safari') == -1 //是否web应该程序，没有头部与底部
            };
        }(),
        language: (navigator.browserLanguage || navigator.language).toLowerCase()
    };
    /**
     * 请求数据
     * 返回的数据结构：
     *   - success {bool}
     *   - data 将作为 drawDomFn 的参数
     * @param {object|string=} _opts
     *   object: drawDomFn,args,showloading,argToJson 均失效。
     *   - opts.drawDomFn(data) {function|undefined=} drawDomFn 数据绘制函数(参数：数据集合)，其中 this 指向调用 request 方法的元素
     *   - opts.showloading {bool=} 请求时是否显示 loading 动画，默认不显示
     *   - opts.argToJson {bool=} 请求时是否附加如下参数 dataType:"json",contentType:"application/json"
     *   - opts.ajaxOpts {object=} 调用 $.ajax() 请求时的自定义配置
     *   string: 数据源请求jsonUrl（不推荐此用法）
     * @param {function|undefined=} drawDomFn 同 opts.drawDomFn。
     * @param {object=} args  同 opts.ajaxOpts.data
     * @param {bool=} showloading 同 opts.showloading
     * @param {bool=} argToJson 设为 true 时，同 opts.argToJson
     */
    $.fn.request=function(_opts,drawDomFn,args,showloading,argToJson){
        var _this=this
            ,opts;
        /* 参数处理 */
        if(Object.prototype.toString.call(_opts) === '[object String]'){
            opts = {
                url:_opts
                ,data:args
            };
        }else if(_opts){
            showloading=_opts['showloading'];
            drawDomFn=_opts['drawDomFn'];
            argToJson=_opts['argToJson'];
            opts=_opts['ajaxOpts']||{};
        }
        if(argToJson){
            opts.contentType= "application/json";
        }
        _request();
        function _request(){// true
            var errorMsg=_opts['errorMsg']||'网络异常，请稍后再试';
            var _success=opts.success
                ,_error=opts.error;
            showloading && $.showLoading();
            delete opts.success;
            delete opts.error;
            $.ajax($.extend({
                success:function(result,textStatus,jqXHR){
                    showloading && $.hideLoading();
                    if(!result.success){
                        var msg=result.msg||errorMsg;
                        msg && $.showAlert(msg);
                    }else if(drawDomFn){
                        drawDomFn.call(_this,result.data);
                    }
                    _success && _success(result,textStatus,jqXHR);
                }
                ,error:function(jqXHR,textStatus,errorThrown){
                    showloading && $.hideLoading();
                    errorMsg && $.showAlert(errorMsg);
                    _error && _error(jqXHR,textStatus,errorThrown);
                }
                ,type:'post'
                ,dataType:'json'
            },opts));
        }
        return _this;
    };
    /**
     * 获取元素计算后的指定style
     * @param name {string} 如:'width','color'
     * @returns {*}
     */
    $.fn.getStyle=function(name){
        var dom=$(this)[0];
        if (dom.currentStyle) { //IE
            return dom.currentStyle[name];
        } else if (window.getComputedStyle) {
            return window.getComputedStyle(dom)[name];// dom元素,伪元素字符串（如“:after”）
        }
        return dom.style[name];// finally try and get inline style
    };
    /**
     * 分页：对象为 pageBar 容器 class="manu"
     * @param {object} _opts
     *   - opts.per {int=} 每页显示的数据条数，默认为 20，若定义了 opts.perList，opts.per 必须为其中之一，否则默认为 opts.perList[0]
     *   - opts.perList:[] 可选择的每页页码列表，若未定义则不可切换
     *   - opts.afterFlip 每次翻页后的回调函数 function(datalist,pageObj,data)
     *       datalist {Array} 请求页所有数据
     *       pageObj {object} 分页对象
     *         pageObj.totalpage 总页数
     *         pageObj.curpage 当前页码（从1开始）
     *         pageObj.percount 每页数据条数
     *         pageObj.totalcount 总数据数
     *       data {object} 后台分页时，每次分页请求到的后台数据的 data 部分。前台分页时，所有数据的集合
     *   前台分页时：
     *   - opts.data {Array=} 前台分页时的数据源
     *   后台分页时：仅当 data 未定义
     *   - opts.url {string=} 请求 url
     *   - opts.args {object=} 请求参数
     *   - opts.showloading {bool=} 后台分页请求时是否显示加载动画，默认不加载
     * @return {object|undefined} obj
     *   - obj.destroy() {function} 销毁
     *   - obj.page {object} 获得分页信息
     *       obj.page.percount 每页条数
     *       obj.page.totalpage 总页数
     *       obj.page.curpage 当前页码
     *       obj.page.totalcount 总数据条数
     */
    $.fn.initPageBar=function(_opts){
        var CONF={
            visiblePages:5// 页码数量（奇数）
        };
        var el=$(this);
        if(!_opts.data && !_opts.url) return;
        var opts= $.extend({
            per:20// 每页显示的数据条数
//            ,url:''// 后台分页时请求的url
            ,args:{}// 后台分页时的参数
            ,showloading:false// 后台分页请求时是否显示加载动画
//            ,data:[]// 前台分页时的数据源
            ,perList:[]// 可选择的每页页码列表，若未定义则不可切换
//            ,afterFlip:function(datalist,pageObj,data)// 每次翻页后的回调函数，datalist:当前页数据；pageObj:当前分页信息；data
        },_opts);
        var perList,_page={};
        var _init=function(){
            perList=opts.perList;
            if(perList.length && perList.indexOf(opts.per)==-1 && perList[0]){// 定义了每页显示条数列表，且 per 不在 perList 中
                opts.per=perList[0];
            }
            _page.percount=opts.per;
            goPage(1);
        };
        var _destroy=function(){
            el.find('.manu-sel').off('change.per');
            el.off('click.toPage').html('');
        };
        var _reset=function(cusOpts){
            opts= $.extend(opts,cusOpts);
            if(cusOpts.data){
                opts.url=undefined;
            }else if(cusOpts.url){
                opts.data=undefined;
            }
            _init();
        };
        var _returnObj;
        /**
         * 绘制分页及触发回调
         * @param datalist {Array} 请求页所有数据
         * @param pageNum {int} 页码(从 1 开始)
         * @param totalcount {int} 数据总数
         * @param data {object=} 后台分页时，每次分页请求到的后台数据的 data 部分
         */
        var drawDOM=function(datalist,pageNum,totalcount,data){
            var half = (CONF.visiblePages-1)/ 2,pStart,pEnd
                ,totalPage = Math.ceil(totalcount/opts.per)
                ,toPage = pageNum;
            var bakStr, i,pageStr='';// 临时变量
            if(!totalPage){// 总数据为 0，分页信息不需绘制
                pageStr+='<div class="manu-l"><div class="manu-item">共 0 页 0 条</div></div>';
            }else{
                if(toPage > totalPage || toPage < 1){// 页码超出范围，不绘制 DOM
                    $.showAlert('页码超过范围!');
                    return;
                }
                /****** manu-l ******/
                pageStr+='<div class="manu-l">';
                pageStr+='<div class="manu-item">共 '+ totalPage +' 页 '+ totalcount +' 条</div>';
                if(perList && perList.length){// 定义了每页显示条数列表
                    bakStr='<select class="txt-min manu-sel">';
                    for(i=0;i<perList.length;i++){
                        bakStr += '<option'+(perList[i]==opts.per ? ' selected' : '')+'>'+perList[i]+'</option>';
                    }
                    bakStr+='</select>'
                }else{
                    bakStr=opts.per;
                }
                pageStr+='<div class="manu-item">每页 '+bakStr+' 条</div>' +
                    '<div class="manu-item">' +
                    '<input class="txt-min manu-txt" type="text" style="width:50px;"> 页 <input type="button" class="btn-min manu-btn" value="跳转">' +
                    '</div>';
                pageStr+='</div>';// manu-l 结束
                /****** manu-r ******/
                pageStr+='<div class="manu-r">';
                /* 绘制分页 */
                if(toPage-half>0 && toPage+half<=totalPage){
                    pStart = toPage-half;
                    pEnd = toPage+half;
                }else if(toPage-half<=0){
                    pStart = 1;
                    pEnd = Math.min(CONF.visiblePages,totalPage);
                }else{
                    pStart = Math.max(1,totalPage-CONF.visiblePages+1);
                    pEnd = totalPage;
                }
                pageStr += toPage==1 ?
                    ('<span class="manu-lnk manu-lnk-0"><span class="fa fa-angle-double-left"></span></span>' +
                        '<span class="manu-lnk"><span class="fa fa-angle-left"></span></span>') :
                    ('<a href="javascript:void(0)" class="manu-lnk manu-lnk-0" data-page="1"><span class="fa fa-angle-double-left"></span></a>' +
                        '<a href="javascript:void(0)" class="manu-lnk" data-page="'+(toPage-1)+'"><span class="fa fa-angle-left"></span></a>');
                for(i=pStart;i<=pEnd;i++){
                    if(i==toPage){
                        pageStr += '<span class="manu-lnk manu-lnk-i">'+i+'</span>';
                    }else{
                        pageStr += '<a class="manu-lnk" href="javascript:void(0)" data-page="'+i+'">'+i+'</a>';
                    }
                }
                pageStr += toPage>=totalPage ?
                    ('<span class="manu-lnk"><span class="fa fa-angle-right"></span></span>' +
                        '<span class="manu-lnk manu-lnk-x"><span class="fa fa-angle-double-right"></span></span>') :
                    ('<a href="javascript:void(0)" class="manu-lnk " data-page="'+(toPage+1)+'"><span class="fa fa-angle-right"></span></a>' +
                        '<a href="javascript:void(0)" class="manu-lnk manu-lnk-x" data-page="'+totalPage+'"><span class="fa fa-angle-double-right"></span></a>');
                pageStr+='';
                pageStr+='</div>';// manu-r 结束
            }
            _page.totalpage=totalPage;
            _page.curpage=toPage;
            _page.totalcount=totalcount;
            el.html(pageStr);
            opts.afterFlip && opts.afterFlip(datalist,_page,data);// 翻页后的回调函数
            el.find('.manu-sel')
                .off('change.per')
                .on('change.per',function(){
                    _page.percount=opts.per=parseInt($(this).val());
                    goPage(1);
                });
        };
        var goPage=function(pageNum){// 跳转到第几页
            var _data=opts.data;
            if(_data){// 前台分页
                var startNo=(pageNum-1)*opts.per
                    ,len=_data.length;
                drawDOM(_data.slice(startNo,Math.min(len,startNo+opts.per)), pageNum, len, _data);
            }else{
                $.fn.request({
                    drawDomFn:function(data){
                        drawDOM(data['retlist'], pageNum, data['totalcount'], data);
                    }
                    ,showloading:opts.showloading
                    ,ajaxOpts:{
                        url:opts.url
                        ,data:$.extend({
                            curpage:pageNum// 从1开始
                            ,percount:opts.per
                        },opts.args)
                    }
                });
            }
        };
        _destroy();
        _returnObj={
            page:_page
            ,destroy:_destroy
            ,reset:_reset
            ,goPage:goPage
        };// 返回的接口对象
        if(!el.hasClass('manu')) el.addClass('manu');
        el.off('click.toPage').on('click.toPage','a.manu-lnk,.manu-btn',function(){
            if(this.tagName.toLocaleLowerCase() == 'a'){
                goPage(parseInt($(this).attr('data-page'))||1);
            }else if($(this).hasClass('manu-btn')){
                var val=parseInt(el.find('.manu-txt').val());
                if(isNaN(val)){
                    $.showAlert('页码必须为数字!');
                }else{
                    goPage(val);
                }
            }
        });
        _init();
        return _returnObj;
    };

    /* 验证辅助方法 */
    var _formatMsg=function(src, args){
        if (args.constructor!= Array) {
            args = [args];
        }
        $.each(args, function(i, n) {
            src = src.replace(new RegExp('\\{'+i+'\\}','g'), n);
        });
        return src;
    };
    /**
     * 验证当前元素
     * @param rules {object}: 要验证的表单元素的验证规则，键值对
     *     rules.key: 验证规则名称
     *     rules.value: 验证规则对应的参数
     * @param messages {object=}: 自定义提示
     *     messages.key: 验证规则名称
     *     messages.value : 对应规则验证不通过时显示的自定义提示
     * @param cb {function=} 验证不通过时再次点击元素时的回调函数
     * @param bindBlur {bool=} 是否绑定失焦验证的事件，默认为 true，非特殊情况不需定义此属性
     * @returns {boolean} 是否通过验证
     */
    $.fn.validate=function(rules,messages,cb,bindBlur){// el 要添加danger样式的元素
        var el=$(this);
        var _errCls='dangerI'
            ,_blurValEv='blur._validate'
            ,_clickValEv='click._valiTip';
        el.off(_blurValEv)
            .off(_clickValEv);
        if(bindBlur!=false){
            el.on(_blurValEv,function(){// 失焦时根据规则校验-暂只考虑文本框
                el.validate(rules,messages,cb);
            });
        }
        var errorRule = '';
        for(var rule in rules){// 逐个验证 rules
            if(rules.hasOwnProperty(rule)){
                var method=$.validator.methods[rule];
                if(method && !method(rules[rule],el)){// 定义了该验证方法且验证结果为 false
                    errorRule || (errorRule = rule);
                    break;
                }
            }
        }
        if(errorRule==''){
            el.removeClass(_errCls);
        }
        else{
            el.addClass(_errCls);
            var msg=(messages && messages[errorRule]) ?
                messages[errorRule]:
                $.validator.messages[errorRule]||'校验未通过';
            msg = _formatMsg(msg,rules[errorRule]);
            el.on(_clickValEv,(function(msg){
                return function(){
                    if(cb){
                        cb(el,msg);
                    }else $.showValiTip(el,msg);// TODO 显示验证信息
                };
            })(msg));
            return false;
        }
        return true;
    };
    /**
     * 对元素子孙元素中的表单元素初始化验证条件
     * @param options
     *  - options.rules {object} 验证规则，键值对
     *      key: 要验证的表单元素的 name 属性的值
     *      value {object}: 要验证的表单元素的验证规则，键值对
     *          key: 验证规则名称
     *          value: 验证规则对应的参数
     *  - options.messages {object=} 自定义验证信息，键值对
     *      key: 要验证的表单元素的 name 属性的值
     *      value {object}: 验证不通过时显示的自定义提示
     *  - options.callback(el,msg) {function} 自定义提示方法
     *      el {string} 验证失败的元素
     *      msg {string} 该元素上的提示信息
     * @returns {object || undefined} returnObj
     *  - returnObj.validateAll() 手动触发当前元素内表单验证的方法
     *  - returnObj.removeRules(name,rules,validate) 移除指定name的元素的验证规则
     *      name {string} 要移除验证规则的元素的 name，也对应初始化时 opts.rules 及 opts.messages 中的key
     *      rules {Array=} 要移除的规则名数组，若未定义则移除全部规则
     *      validate {bool=} 移除部分规则后，是否立即重新验证该元素，默认为 true
     *  - returnObj.addRules(addopts,validate) 移除指定name的元素的验证规则
     *      addopts {object} 同初始化时的options（但不含 callback）
     *      validate {bool=} 移除部分规则后，是否立即重新验证全部元素，默认为 true
     *  - returnObj.resetValStat() 恢复所有验证状态
     * 验证不通过时将对该表单元素的 class 添加 "dangerI"
     */
    $.fn.initValidator=function(options){
        if(!options || !options.rules) return;
        var rules=options.rules
            ,messages=options.messages
            ,form=$(this);
        var _errCls='dangerI'
            ,_blurValEv='blur._validate'
            ,_clickValEv='click._valiTip';// 与 $.fn.validate 一致
        var _validate=function(name){
            var rulesOnEl=rules[name];
            if(!rulesOnEl) return true;// 此元素上未定义规则，验证通过
            var messagesOnEl=messages?messages[name]:undefined;
            return form.find('[name="'+name+'"]').validate(rulesOnEl,messagesOnEl,options.callback,false);
        };
        var _validateAll=function(){
            var status=true;
            for(var name in rules){
                if(rules.hasOwnProperty(name)){
                    if(_validate(name)==false){// 对此元素定义了规则且验证未通过
                        status=false;
                    }
                }
            }
            return status;
        };
        var _init=function(){
            for(var name in rules){
                if(rules.hasOwnProperty(name)){// 设置需要验证的表单元素的属性
                    form.find('[name="'+name+'"]').attr('data-validate','true');
                }
            }
            form
                .off(_blurValEv)
                .on(_blurValEv,'[data-validate="true"]',function(){// 失焦时根据规则校验-暂只考虑文本框
                    _validate($(this).attr('name'));
                })
                .submit(function(){
                    return _validateAll();
                });
        };
        /**
         * 移除指定name的元素的验证规则
         * @param name {string} 要移除验证规则的元素的 name，也对应初始化时 opts.rules 及 opts.messages 中的key
         * @param removeRules {Array=} 要移除的规则名数组，若未定义则移除全部规则
         * @param validate {bool=} 移除部分规则后，是否立即重新验证该元素，默认为 true
         * @private
         */
        var _removeRules=function(name,removeRules,validate){
            var el=form.find('[name="'+name+'"]');
            var rulesOnEl=rules[name];
            var hasProp=false;// 标记移除部分规则后是否还存在验证规则
            if(removeRules && removeRules.length){// 解绑指定规则
                for(var i= 0,len=removeRules.length;i<len;i++){
                    delete rulesOnEl[removeRules[i]];
                }
                for(var rName in rulesOnEl){
                    hasProp=true;// 存在验证规则
                    break;
                }
            }else{// 解绑全部规则
                rulesOnEl=undefined;
            }
            el.removeClass(_errCls);// 删除原验证状态
            el.off(_clickValEv);
            if(!hasProp){
                el.removeAttr('data-validate');
                delete rules[name];
            }else if(validate!=false){// 有规则，并定义了立即重新验证
                _validate(name);
            }
        };
        /**
         * 移除指定name的元素的验证规则
         * @param addOpts {object} 同初始化时的options（但不含 callback）
         * @param validate {bool=} 移除部分规则后，是否立即重新验证全部元素，默认为 true
         * @private
         */
        var _addRules=function(addOpts,validate){
            var _rules=addOpts.rules
                ,_messages=addOpts.messages;
            for(var name in _rules){
                if(_rules.hasOwnProperty(name)){// 设置需要验证的表单元素的属性
                    var el=form.find('[name="'+name+'"]');
                    el.attr('data-validate','true');
                    rules[name] = rules[name] ?
                        $.extend(rules[name],_rules[name]) : _rules[name];
                    if(validate==false){// 不立即验证，取消之前的验证结果
                        el.removeClass(_errCls);// 删除原验证状态，与 $.fn.validate 中一致
                        el.off(_clickValEv);// 与 $.fn.validate 中一致
                    }
                }
            }
            if(_messages){
                if(_messages.hasOwnProperty(name)){
                    messages[name] = messages[name] ?
                        $.extend(messages[name],_messages[name]) : _messages[name];
                }
            }
            if(validate!=false){// 立即验证全部
                _validateAll();
            }
        };
        /**
         * 恢复所有验证状态
         * @private
         */
        var _resetValStat=function(){
            form.find('[data-validate="true"]')
                .removeClass(_errCls)// 与 $.fn.validate 中一致
                .off(_clickValEv);// 与 $.fn.validate 中一致
        };
        var _returnObj={
            el:form
            ,validateAll: _validateAll
            ,removeRules: _removeRules
            ,addRules: _addRules
            ,resetValStat: _resetValStat
        };
        _init();
        return _returnObj;
    };
    /**
     * 显示 el 上验证提示信息 msg
     * @param el
     * @param msg
     * @param opts {object=}
     */
    $.showValiTip=function(el,msg,opts){
        var vEl;
        var _close=function(){
            if(!vEl) return;
            vEl.off('click.valOverlay');
            vEl.remove();
        };
        var init=function(){
            vEl=$('<div class="overlay"><div class="overlay-msg">'+msg+'</div></div>');
            vEl.on('click.valOverlay',function(){// 点击遮罩层关闭
                if($(this).parent('body').length>0) _close();
            });
            $('body').append(vEl);
            setTimeout(_close, 2000);
        };
        init();
    };

    /**
     * 显示 alert 弹框
     * @param msg {string} 提示信息
     * @param cb {function=} 关闭提示信息时的回调函数
     * @param win {object=} 弹框时参照的 window 对象，默认为当前 window。
     */
    $.showAlert=function(msg,cb,win){
        alert(msg);
        cb && cb();
    };
    /**
     * 显示 msg 弹框
     * @param msg {string} 提示信息
     * @param cb {function=} 关闭时的回调函数
     * @param win {object=} 弹框时参照的 window 对象，默认为当前 window。
     */
    $.showMsg=function(msg,cb,win){
        var el;
        var _close=function(){
            if(!el) return;
            el.off('click.overlay');
            el.remove();
            cb && cb();
        };
        var init=function(){
            el=$('<div class="overlay overlay-modal"><div class="overlay-msg">'+msg+'</div></div>');
            el.on('click.overlay',function(e){// 点击遮罩层关闭
                if($(e.target).parent('body').length>0) _close();
            });
            if(!el.parent('body').length) $('body').append(el);
            setTimeout(_close, 2000);
        };
        init();
    };
    /**
     * 计算包含中英文字符混合的字符串的长度
     * @param str 要计算长度的字符串
     * @returns {number}
     */
    $.getZhStrLength=function(str){
        var totalLength = 0;
        if(!!str) {
            var list = str.split("");
            for(var i = 0; i < list.length; i++) {
                var s = list[i];
                if (s.match(/[\u0000-\u00ff]/g)) {//半角
                    totalLength += 1;
                } else if (s.match(/[\u4e00-\u9fa5]/g)) {//中文
                    totalLength += 2;
                } else if (s.match(/[\uff00-\uffff]/g)) {//全角
                    totalLength += 2;
                }
            }
        }
        return totalLength;
    };
    /* 验证规则定义 */
    $.validator={
        messages: {
            'required': '此字段必填'
            ,'requiredNoTrim': '此字段必填'
            ,'number': '此字段必须为数字'
            ,'int': '此字段必须为整数'
            ,'maxlength': '此字段值最多{0}个字符'
            ,'length': '此字段值为{0}个字符'
            ,'ZhStrMaxLength': '此字段最多{0}个字符（中文计为2个字符）'
            ,'ZhStrRangeLength': '此字段限制{0}-{1}个字符（中文计为2个字符）'
            ,'isPhone': '电话号码格式错误'
            ,'isCardNo': '身份证号格式错误'
            ,'regularName':'此字段只能由字母、数字、下划线组成'
        },
        // 验证通过时返回 true，不通过时返回 false
        methods: {
            'required': function(limit, el) {
                if(limit){
                    var val = el.val().trim();
                    return val && val.length > 0;
                }
                return true;
            }
            // 必填，但允许前后空格
            ,'requiredNoTrim': function(limit, el) {
                if(limit){
                    var val = el.val();
                    return val && val.length > 0;
                }
                return true;
            }
            ,'number': function(limit, el) {
                if(limit){
                    var val = el.val();
                    return !val || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(val);
                }
                return true;
            }
            ,'int': function(limit, el) {
                if(limit){
                    var val = el.val();
                    return !val || /^-?\d+$/.test(val);
                }
                return true;
            }
            ,'maxlength': function(limit, el) {
                if(typeof limit=='number'){
                    var val = el.val();
                    return !val || val.length <= limit;
                }
                return true;
            }
            ,'length': function(limit, el) {
                if(typeof limit=='number'){
                    var val = el.val();
                    return !val || val.length == limit;
                }
                return true;
            }
            // 字符最大长度验证（一个中文字符长度为2）
            ,'ZhStrMaxLength': function(limit, el){
                if(typeof limit=='number'){
                    var val = el.val();
                    return !val || $.getZhStrLength(val)<=limit;
                }
                return true;
            }
            // 字符长度区间验证（一个中文字符长度为2）不能在用class属性定义验证规则时使用,取不到区间的值
            ,'ZhStrRangeLength': function(limit, el){
                if(limit.constructor==Array){
                    var val = el.val()
                        ,length = $.getZhStrLength(val);
                    return !val || (length >=limit[0] && length <= limit[1]);
                }
                return true;
            }
            // 联系电话(手机/电话皆可)验证
            ,'isPhone': function(limit, el){
                if(limit){
                    var val = el.val()
                        ,length = val.length
                        ,mobileRex = /^(1[3|4|5|8][0-9]{1})+\d{8}$/
                        ,telRex = /^(0[0-9]{2,3}\-?)?([2-9][0-9]{6,7})+(\-[0-9]{1,4})?$/;
                    return !val || (telRex.test(val) || (length == 11 && mobileRex.test(val)));
                }
                return true;
            }
            // 身份证号码为15位或者18位，15位时全为数字，18位前17位为数字，最后一位是校验位，可能为数字或字符X
            ,'isCardNo':function(limit, el){
                var _cardNoVali=function(idcard){
                    idcard = idcard.toString().toUpperCase();// 将末位的x装换成X
                    // 身份证号码为15位或者18位，15位时全为数字，18位前17位为数字，最后一位是校验位，可能为数字或字符X
                    if(!(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/i.test(idcard))) return false;
                    var paritybit = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];// 校验位取值
                    var power_list = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];// 加权因子

                    var fifteenToEighteen=function(str){
                        var s = 0;
                        var newid = str.substring(0,6) + "19" + str.substring(6,str.length);
                        for (var i=0;i < newid.length;i++ ){
                            s += parseInt(newid.substring(i,i+1))*power_list[i];
                        }
                        return newid+paritybit[s%11];
                    };
                    /**验证1位校验码（18位）*/
                    var checkcodeValidation=function(str){
                        for (var i=0,s=0;i < str.length-1;i++ ){
                            s += parseInt(str[i])*power_list[i];
                        }
                        return paritybit[s % 11] == str[17];
                    };
                    /**验证6位地址码（前6位）? 2位*/
                    var locationValidation=function(str){
                        var provincesAndCities=['11','12','13','14','15','21','22',
                            '23','31','32','33','34','35','36',
                            '37','41','42','43','44','45','46',
                            '50','51','52','53','54','61','62',
                            '63','64','65','71','81','82','91'];/**省、直辖市代码*/
                        return provincesAndCities.indexOf(str)!=-1;
                    };
                    /**验证8位生日数字（7到14位）*/
                    var birthdayValidation=function(str){
                        var year = str.substring(0,4);
                        var month = str.substring(4,6);
                        var day = str.substring(6,8);
                        var birthday =  year+"-"+month+"-"+day;
                        var date = new Date(year,parseInt(month)-1,day);
                        /**大于等于当前日期 或 小于1900年1月1日*/
                        if(date >= new Date() || date <= new Date(1900,0,1)) return false;
                        return date.format( "yyyy-MM-dd")==birthday;
                    };
                    if(15 == idcard.length){// 15位转18位
                        idcard = fifteenToEighteen(idcard);
                    }
                    return (locationValidation(idcard.substring(0,2))// 验证6位地址码（前6位）? 2位
                        && birthdayValidation(idcard.substring(6,14))// 验证8位生日数字（7到14位）
                        && checkcodeValidation(idcard));// 验证1位校验码（18位）
                };
                if(limit){
                    var val = el.val();
                    return !val || _cardNoVali(val);
                }
                return true;
            }
            ,'regularName':function(limit, el){
                if(limit){
                    var val = el.val()
                        ,cardNoRex = /(^[\da-zA-Z_]+$)/;
                    return !val || cardNoRex.test(val);
                }
                return true;
            }
        },
        /**
         * 扩展验证对象
         * @param ruleName {string} 规则名
         * @param ruleMethod {function} 验证方法
         * @param msg {string} 默认提示信息
         */
        add:function(ruleName,ruleMethod,msg){
            $.validator.messages[ruleName]=msg;
            $.validator.methods[ruleName]=ruleMethod;
        }
    };
    /**
     * 获取当前日期 TODO 扩展为从服务器获取
     * return {Date}
     */
    $.getDate=function(){
        return new Date();
    };
    /**
     * 获取元素中定义了 name 属性的元素的 value，调用此方法的元素必须为 form
     * @returns {Object} 键值对
     */
    $.fn.serializeObject=function(){
        var o = {};
        var a = this.serializeArray();
        $.each(a, function() {
            if(this.name!='' && this.name!=null){
                if (o[this.name] != undefined) {
                    if (!o[this.name].push) {
                        o[this.name] = [o[this.name]];
                    }
                    o[this.name].push(this.value || '');
                } else {
                    o[this.name] = this.value || '';
                }
            }
        });
        return o;
    };
    /**
     * 显示 loading 动画
     */
    $.showLoading=function(){
        if($('body>.loadingdata').length<=0){
            $('body').append('<div class="loadingdata"></div>');
        }
    };
    /**
     * 隐藏 loading 动画
     */
    $.hideLoading=function(){
        var loading=$('body>.loadingdata');
        if(loading.length>0){
            loading.remove();
        }
    };

    /**
     * 初始化 dragloader
     * @param _opts
     * - opts.threshold {number=} helper中内容高度，默认为 40
     * - opts.dragDownThreshold {number=} 向下拖拽时helper中内容高度，默认为 opts.threshold
     * - opts.dragUpThreshold {number=} 同上
     * - opts.disableDragDown {bool=} 是否禁止向下拖拽，默认为 false
     * - opts.disableDragUp {bool=} 同上
     * - opts.beforeDrag {function=} 拖拽生效前的回调函数，若返回 true，则阻止拖拽
     * - opts.dragDownHelper {function=} 向下滑动时helper中dom字符串，参数：status（'default','prepare','load'）
     * - opts.dragUpHelper {function=} 同上
     * - opts.onDragDownStatus {function=} 向下滑动时切换状态时的回调，参数：dragger（组件返回对象）,status（切换到的新状态-'default','prepare','load'）
     * - opts.onDragUpStatus {function=} 同上
     * @returns {object} obj
     * - obj.reset() 还原状态
     * - obj.setDragDownDisabled(disabled) 设置 opts.disableDragDown
     * - obj.setDragUpDisabled(disabled) 同上
     * - obj.destroy() 销毁
     */
    $.fn.initDragLoader=function(_opts){
        var el=$(this);
        var opts= $.extend({
            threshold:40// hold 高度
//        ,dragDownThreshold:threshold
//        ,dragUpThreshold:threshold
            ,disableDragDown:false
            ,disableDragUp:false
            ,beforeDrag:$.noop
//            ,dragDownHelper: function// 返回向下滑动时helper中dom字符串，参数：status（'default','prepare','load'）
//            ,dragUpHelper: function
            ,onDragDownStatus: $.noop// 向下滑动时切换状态时的回调，参数：dragger,status（切换到的新状态-'default','prepare','load'）
            ,onDragUpStatus: $.noop
        },_opts);

        /* 辅助功能 */
        var msPointerEnabled = window.navigator.msPointerEnabled
            ,TOUCH_EVENTS = {
                start: (msPointerEnabled ? 'MSPointerDown' : 'touchstart') + '.dragLoader',
                move: (msPointerEnabled ? 'MSPointerMove' : 'touchmove') + '.dragLoader',
                end: (msPointerEnabled ? 'MSPointerUp' : 'touchend') + '.dragLoader'
            };
        var PREFIX=(function(){
                var dummyStyle = document.createElement('div').style;
                var vendors = 't,webkitT,MozT,msT,OT'.split(',');
                for (var t,i= 0,l=vendors.length; i < l; i++) {
                    t = vendors[i] + 'ransform';
                    if (t in dummyStyle) {
                        return vendors[i].substr(0, vendors[i].length - 1);
                    }
                }
                return false;
            })()
            ,_transName=(function(style){
                if (PREFIX === '') return style;
                style = style.charAt(0).toUpperCase() + style.substr(1);
                return PREFIX + style;
            })('transition');
        var transEndEvent = (function() {
            if (PREFIX == 'webkit' || PREFIX === 'O') {
                return PREFIX.toLowerCase() + 'TransitionEnd';
            }
            return 'transitionend';
        }());
        var listenTransition = function(target, duration, callbackFn) {
            var clear = function() {
                    if (target.transitionTimer) clearTimeout(target.transitionTimer);
                    target.transitionTimer = null;
                    target.removeEventListener(transEndEvent, handler, false);
                },
                handler = function() {
                    clear();
                    callbackFn && callbackFn();
                };
            clear();
            target.addEventListener(transEndEvent, handler, false);
            target.transitionTimer = setTimeout(handler, duration + 100);
        };

        /* 属性初始化 */
        if (!opts.dragDownThreshold) opts.dragDownThreshold = opts.threshold;
        if (!opts.dragUpThreshold) opts.dragUpThreshold = opts.threshold;
        if (!opts.dragDownHelper){
            opts.dragDownHelper=function(status) {
                if (status == 'default') {
                    return '下拉加载最新';
                } else if (status == 'prepare') {
                    return '释放刷新';
                } else if (status == 'load') {
                    return '加载中...';
                }
            }
        }
        if(!opts.dragUpHelper){
            opts.dragUpHelper=function(status) {
                if (status == 'default') {
                    return '向上拉加载更多';
                } else if (status == 'prepare') {
                    return '释放刷新';
                } else if (status == 'load') {
                    return '加载中...';
                }
            }
        }

        var header,footer,cont;
        var canDragDown,canDragUp;// start 时计算当前是否允许拖拽
        var _draggable=true;
        var _coords=null;

        var init=function(){
            if((el.getStyle('position') || 'static' ) === 'static'){
                el.css('position','relative');
            }
            cont=el.children().eq(0);
            header=$('<div class="dragBefore">' +
                '<div class="dragStatus"><div class="dragIco"></div><span class="dragTxt"></span></div>' +
                '</div>');
            footer=$('<div class="dragAfter">' +
                '<div class="dragStatus"><div class="dragIco"></div><span class="dragTxt"></span></div>' +
                '</div>');
            el.prepend(header);
            el.append(footer);
            el.on(TOUCH_EVENTS.start,_onTouchStart);
        };

        var _onTouchStart=function(e) {
            if(!_draggable) return;// 已经在拖拽的过程中
            var startScrollY=cont.scrollTop();// 内容部分 scrollTop
            canDragDown = opts.disableDragDown !== true && startScrollY <= 0;// 是否允许向下拖拽
            canDragUp = opts.disableDragUp !== true && startScrollY + cont.height() >= cont[0].scrollHeight;// 是否允许向上拖拽
            if ((canDragDown || canDragUp) && opts.beforeDrag() !== false){// 允许拖拽，且未被拦截
                _draggable = false;
                var doc=$(document);
                doc.off(TOUCH_EVENTS.move);
                doc.off(TOUCH_EVENTS.end);
                doc.on(TOUCH_EVENTS.move, _onTouchMove);
                doc.on(TOUCH_EVENTS.end, _onTouchEnd);
                _coords = {};
                if(msPointerEnabled){
                    _coords.startY = e.screenY;
                    _coords.identifier = e.pointerId;// 记录触摸对象
                }else{
                    var touch= e.targetTouches[0];
                    _coords.startY = touch.screenY;
                    _coords.identifier = touch.identifier;// 记录触摸对象
                }
                _coords.startScrollY = startScrollY;
            }
        };

        var _findTouch=function(touches){
            if(!_coords) return;
            for(var i= 0;i<touches.length;i++){
                if(touches[i].identifier === _coords.identifier){// 触发 start 的 touch 对象未释放
                    return touches[i];
                }
            }
        };

        var _onTouchMove=function(e){
            var stopY,clientX,clientY;
            // 判断是否为 start 的触摸点移动
            if(msPointerEnabled){
                if(e.pointerId == _coords.identifier){
                    stopY=e.screenY;
                    clientX=e.clientX;
                    clientY=e.clientY;
                }else return;
            }else{
                var touch=_findTouch(e.changedTouches);
                if(touch){
                    stopY=touch.screenY;
                    clientX=touch.clientX;
                    clientY=touch.clientY;
                }else return;
            }
            var startY = _coords.startY
                ,offsetY = stopY - startY;
            var winW=window.innerWidth
                ,winH=window.innerHeight;
            if (canDragDown && offsetY>0) {// 向下滑动
                e.preventDefault();
                _coords.orient='down';
                _hideHelper(footer);
                header.addClass('active');
                header.css('height',offsetY+'px');
                _processStatus(offsetY, true);
            } else if (canDragUp && offsetY<0) {
                e.preventDefault();
                _coords.orient='up';
                _hideHelper(header);
                footer.addClass('active');
                offsetY = -offsetY;
                footer.css('height',offsetY+'px');
                el.scrollTop(offsetY);
                _processStatus(offsetY, true);
            }
            if($.browser.versions.ios){// ios 中通过move时进入边界触发end事件，防止移出屏幕时无反应
                var borderSize=Math.min(winW,winH)/10;
                if(clientX<borderSize || clientY<borderSize || clientX>winW-borderSize || clientY>winH-borderSize){
                    _onTouchEnd();
                }
            }
        };
        var _onTouchEnd=function(e){
            if(_draggable==false){
                // 由 touchend 事件触发 - 之所以需要判断是因为 touchmove 中可能因手指靠近边界调用 _onTouchEnd 结束 drag
                if(!e || !e.changedTouches || !!_findTouch(e.changedTouches)){
                    var doc=$(document);
                    doc.off(TOUCH_EVENTS.move);
                    doc.off(TOUCH_EVENTS.end);
                    _translate();
                }
            }
        };

        /**
         * @param offsetY {number} helper 容器高度
         * @param moving {bool} 是否正在移动
         * @returns {*}
         * @private
         */
        var _processStatus= function(offsetY, moving) {
            var orient=_coords.orient// down|up
                ,status = _coords.status;// undefined,'default','prepare','load'
            if (orient) {
                var upperStr = orient.charAt(0).toUpperCase() + orient.substr(1)// 首字母转大写
                    ,overflow = offsetY > opts['drag' + upperStr + 'Threshold'];// helper尺寸是否已经超过内容尺寸
                if (!overflow && status != 'default') {
                    status = 'default';
                } else if (moving && overflow && status != 'prepare') {
                    status = 'prepare';
                } else if (!moving && overflow && status != 'load') {
                    status = 'load';
                }
                if(status != _coords.status){
                    _coords.status=status;
                    var helper = opts['drag'+upperStr+'Helper'];
                    if (helper) {
                        if(orient=='down'){
                            header.find('.dragTxt').html(helper(status));
                            header.find('.dragStatus').attr('data-drag',status);
                        }else{
                            footer.find('.dragTxt').html(helper(status));
                            footer.find('.dragStatus').attr('data-drag',status);
                        }
                    }
                    opts['onDrag' + upperStr + 'Status'](status,returnObj);
                }
            }
            return status;
        };

        var _hideHelper=function(helper){
            helper.removeClass('active');
            helper.css('height',0);
        };

        var _translate= function() {
            if (!_coords) return;
            var orient = _coords.orient
                ,offsetY;
            var endFn = function() {// 隐藏 helper 并重置 _coords、_draggable
                _processStatus(offsetY, false);
                if (!orient || _coords.status !== 'load') {
                    _hideHelper(header);
                    _hideHelper(footer);
                    _coords = null;
                    _draggable = true;
                } else if (orient == 'down') {
                    _hideHelper(footer);
                } else if (orient == 'up') {
                    _hideHelper(header);
                }
            };
            if (orient) {
                var maxDuration = 200
                    ,helperEl = orient == 'down' ? header : footer
                    ,upperStr = orient.charAt(0).toUpperCase() + orient.substr(1)
                    ,threshold = opts['drag' + upperStr + 'Threshold'];// helper 内容高度
                offsetY = helperEl.height();// helper 高度
                var adjustHeight = offsetY > threshold ? threshold : 0
                    ,duration = Math.ceil((offsetY - adjustHeight) / threshold * maxDuration);
                duration = duration > maxDuration ? maxDuration : duration;// 拉回 helper 的动画时间
                listenTransition(helperEl[0], duration, endFn);
                helperEl.css(_transName,'height ' + duration + 'ms');
                setTimeout(function() {
                    helperEl[0].style.height = adjustHeight + 'px';
                    helperEl.css(_transName,'');
                }, 0);
            } else {
                endFn();
            }
        };
        var _destroy = function() {
            header.remove();
            footer.remove();
            el.off(TOUCH_EVENTS.start);
            var doc=$(document);
            doc.off(TOUCH_EVENTS.move);
            doc.off(TOUCH_EVENTS.end);
        };
        var returnObj={
            reset:_translate
            ,setDragDownDisabled: function(disabled) {
                opts.disableDragDown = disabled;
            }
            ,setDragUpDisabled: function(disabled) {
                opts.disableDragUp = disabled;
            }
            ,destroy:_destroy
        };
        init();
        return returnObj;
    };
    /**
     * 拖拽分页：将在el中添加 header,footer，el中因只有一个高度为100%的非定位元素
     * @param {object} _opts
     *   - opts.per {int=} 每页显示的数据条数，默认为 20
     *   - opts.afterFlip 每次翻页后的回调函数 function(datalist,pageObj,data)
     *       datalist {Array} 请求页所有数据
     *       pageObj {object} 分页对象
     *         pageObj.totalpage 总页数
     *         pageObj.curpage 当前页码（从1开始）
     *         pageObj.percount 每页数据条数
     *         pageObj.totalcount 总数据数
     *       data {object} 后台分页时，每次分页请求到的后台数据的 data 部分。前台分页时，所有数据的集合
     *   前台分页时：
     *   - opts.data {Array=} 前台分页时的数据源
     *   后台分页时：仅当 data 未定义
     *   - opts.url {string=} 请求 url
     *   - opts.args {object=} 请求参数
     *   - opts.showloading {bool=} 后台分页请求时是否显示加载动画，默认为 false
     * @return {object|undefined} obj
     *   - obj.reset(opts) {function} 重置对象
     *   - obj.page {object} 获得分页信息
     *       obj.page.percount 每页条数
     *       obj.page.totalpage 总页数
     *       obj.page.curpage 当前页码
     *       obj.page.totalcount 总数据条数
     */
    $.fn.initDragList=function(_opts){
        var el=$(this);
        if(!_opts.data && !_opts.url) return;
        var opts= $.extend({
            per:20// 每页显示的数据条数
//            ,url:''// 后台分页时请求的url
            ,args:{}// 后台分页时的参数
            ,showloading:false// 后台分页请求时是否显示加载动画
//            ,data:[]// 前台分页时的数据源
//            ,afterFlip:function(datalist,pageObj,data)// 每次翻页后的回调函数，datalist:当前页数据；pageObj:当前分页信息；data
            ,delay:1000// 加载动画保持时间
        },_opts);
        var _page={};
        var dragLoaderObj;
        var _init=function(){
            _page.percount=opts.per;
            if(!dragLoaderObj){
                dragLoaderObj=el.initDragLoader({
                    onDragDownStatus: function(status,dragger){
                        if(status=='load'){
                            goPage(1,true);
                        }
                    }
                    ,onDragUpStatus: function(status,dragger){
                        if(status=='load'){
                            goPage(_page.curpage+1);
                        }
                    }
                });
            }
            goPage(1,true);
        };
        var _reset=function(cusOpts){
            opts= $.extend(opts,cusOpts);
            if(cusOpts.data){
                opts.url=undefined;
            }else if(cusOpts.url){
                opts.data=undefined;
            }
            _init();
        };
        var _returnObj;
        /**
         * 绘制分页及触发回调
         * @param datalist {Array} 请求页所有数据
         * @param pageNum {int} 页码(从 1 开始)
         * @param totalcount {int} 数据总数
         * @param data {object=} 后台分页时，每次分页请求到的后台数据的 data 部分
         */
        var drawDOM=function(datalist,pageNum,totalcount,data){
            _page.totalpage=Math.ceil(totalcount/opts.per);
            _page.curpage=pageNum;
            _page.totalcount=totalcount;
            dragLoaderObj.setDragUpDisabled(pageNum==_page.totalpage);// 当前页是最后一页，禁用想上拖拽，否则启用
            opts.afterFlip && opts.afterFlip(datalist,_page,data);// 翻页后的回调函数
            setTimeout(function(){
                dragLoaderObj.reset();
            },opts.delay);
        };
        /* init 是否是初次请求（第一页） */
        var goPage=function(pageNum,init){// 跳转到第几页
            var _data=opts.data;
            if(!init && _page.totalpage!=undefined && pageNum>_page.totalpage) return;// 已经完成最后一页的请求
            if(_data){// 前台分页
                var startNo=(pageNum-1)*opts.per
                    ,len=_data.length;
                drawDOM(_data.slice(startNo,Math.min(len,startNo+opts.per)), pageNum, len, _data);
            }else{
                $.fn.request({
                    drawDomFn:function(data){
                        drawDOM(data['retlist'], pageNum, data['totalcount'], data);
                    }
                    ,showloading:opts.showloading
                    ,ajaxOpts:{
                        url:opts.url
                        ,data:$.extend({
                            curpage:pageNum// 从1开始
                            ,percount:opts.per
                        },opts.args)
                    }
                });
            }
        };
        _returnObj={
            page:_page
            ,reset:_reset
        };// 返回的接口对象
        _init();
        return _returnObj;
    };
    /**
     * 初始化表格，当前元素中必须包含 .listtableDiv
     * @param _opts {object}
     *  - opts.url {string} 请求路径
     *  - opts.args {object=} 请求参数的键值对
     *  - opts.showHead {bool=} 是否显示表头，默认为 true
     *  - opts.showno {bool=} 是否显示序号，默认为 false
     *  - opts.chkField {bool|string=} 若定义此属性，则显示复选框列。若值为 string，则根据 chkField 对应的值初始化选中状态。
     *  - opts.singleChk {bool=} 若定义为 true，则只能进行单选，默认为 false（多选时必须定义 opts.chkField）
     *  - opts.page {object} 分页数据
     *      opts.page.percount {int} 默认每页数据条数
     *      opts.page.perList {Array} 分页数据列表
     *  - opts.filterEl {object} 过滤文本框对象
     *  - opts.filter {object=} 过滤功能配置，仅当定义了 filterEl 时生效
     *      opts.filter.fields {string|Array=} 参与过滤的列的属性名，对应 cols[i].field
     *      opts.filter.pySupport {bool=} 是否支持拼音首字过滤，开启过滤时，默认为 true
     *      opts.filter.hide {bool=} 是否隐藏不符合过滤条件的行，默认为 true，复选时此属性无效
     *  - opts.prompt {bool=} 无数据时是否显示提示，默认为 true
     *  - opts.cols {Array=} 列定义 [{field,...}]
     *      opts.cols[i].field {string} 该列对应的属性名
     *      opts.cols[i].name {string=} 该列的标识，默认与 field 相同
     *      opts.cols[i].title {string} 该列显示的标题文本
     *      opts.cols[i].showTip {bool=} 该列是否显示 title，默认为 false
     *      opts.cols[i].type {string} 该列显示的数据类型，默认为：'string'，可选值：'number','string'
     *      opts.cols[i].width {string} 该列宽度，需带单位
     *      opts.cols[i].align {string} 对齐方式，可选值：'center','left','right'。不指定时，type='string'时为'left'，type='number'时为'right'
     *      opts.cols[i].sort {bool} 该列是否允许排序，默认 false TODO
     *      opts.cols[i].render {function} 该列的渲染函数 render(val,row,i,grid) val 单元格值，row 行数据，i 当前行索引（相对当前页），grid 接口对象；返回 dom 字符串
     *      opts.cols[i].hrender {function} 该列标题部分的渲染函数 hrender(name,title) 当前列的 name 及 title
     *  - opts.rowClser {function=} 行自定义class函数。function(row,i){...} 参数参考 opts.cols[i].render
     *  - opts.callback {object=} 回调函数定义
     *      opts.callback.cell {object=} 单元格事件
     *          cell:{
     *              'ctrl':{     // 对应 cols 中定义的 opts.cols[i].name
     *                  'click':function(e,val,row,i){...}    // this 指向当前单元格 dom 对象，参数参考 opts.cols[i].render，e 为触发事件的 event 对象
     *              }
     *          }
     *      opts.callback.row {object=} 行事件
     *          row:{
     *              'click':function(e,row,i){...}    // this 指向当前行 dom 对象，参数参考 opts.callback.cell
     *          }
     *      opts.callback.afterShowData {function=} 显示完数据的回调函数
     *          function(grid){...}
     *      opts.callback.onCheckRow{function=} 选中行时的回调函数
     *          function(row,i,el){...} 参数依次是：行数据，行索引，行对应的tr对象
     *      opts.callback.onUnCheckRow{function=} 取消行选中时的回调函数
     *          function(row,i,el){...} 参数依次是：行数据，行索引，行对应的tr对象
     * ========================
     * @return {object|undefined} obj 接口对象
     *   - obj.el 获取当前 grid 对应的 el
     *   - obj.findCol(key,name) 根据条件查找所有符合的 cols[i].key=val 的列定义对象集合，未找到则为 []
     *   - obj.findRow(field,val) 根据条件查找所有符合的 data[i].field=val 的行对象集合，未找到则为 []
     *   - obj.getCheckedIdx() 获取当前列表所有选中行索引
     *   - obj.getCheckedRows() 获取当前列表所有选中行
     *   - obj.checkRow(row,chk) 改变指定行的选中状态
     *       row {object|Array} 要选中的行对应的数据，支持多条数据组成的数组
     *       chk {bool} true 时改为选中，false 改为不选中
     *   - obj.setData(data) 重置当前显示的数据列表
     *       data 新的列表数据
     *   - obj.getData() 返回当前显示的数据列表
     *   - obj.getPage() 返回当前的分页信息，返回值 {percount,totalpage,curpage,totalcount}
     *   - obj.resetData(cusOpts) 扩展初始化时的配置，重新请求数据（分页），不重绘整个 Grid
     *   - obj.merge(merges) 合并单元格
     *       merges {Array} 要合并的单元格信息
     *           merges[i].rowI {int} 要合并的单元格在当前页的索引
     *           merges[i].name {string} 要合并的单元格所在列对应的 name
     *           merges[i].rowspan {int} 合并几行
     *           merges[i].colspan {int} 合并几列
     *   - obj.destroy() 销毁 grid
     */
    $.fn.initGrid=function(_opts){
        var el=$(this);
        var opts= $.extend({
            showHead:true
            ,showno: false
            ,showloading: false
//            ,url:''
//            ,data:[]
//            ,args:{}
            ,chkField: false
            ,singleChk: false
            ,page:false
//            ,filterEl
            ,filter:{}
            ,prompt:true
            ,cols:[]
//            ,rowClser:function
//            ,callback:{}
        },_opts);
        if(!el.hasClass('listtableDiv')) el.addClass('listtableDiv');
        var _page=opts.page
            ,_chkField=opts.chkField
            ,_cols=opts.cols
            ,_cb=opts.callback
            ,_data
            ,_curData; // 记录当前页数据
        var _manuObj
            ,_pageObj// initPageBar 中绘制分页信息时回传的 _manuObj.page 更实时
            ,_filterObj
            ,_returnObj;
        /**
         * 根据条件查找所有符合的 cols[i].key=val 的列
         * @param key
         * @param val
         * @return {Array} 所有符合的列定义对象集合，未找到则为 []
         * @private
         */
        var _findCol=function(key,val){
            var idxArr=[];
            for(var i=0;i<_cols.length;i++){
                var col=_cols[i];
                if(col[key]==val){
                    idxArr.push(col);
                }
            }
            return idxArr;
        };
        /**
         * 根据条件查找所有符合的 data[i].field=val 的行
         * @param field
         * @param val
         * @return {Array} 所有符合的行对象集合，未找到则为 []
         * @private
         */
        var _findRow=function(field,val){
            var rows=[];
            for(var i=0;i<_data.length;i++){
                var row=_data[i];
                if(row[field]==val){
                    rows.push(row);
                }
            }
            return rows;
        };
        /**
         * 改变指定行的选中状态
         * @param row {object|Array} 要选中的行对应的数据，必须为当前 grid 中的数据对象
         * @param chk {bool} true 时改为选中，false 改为不选中
         */
        var _checkRow=function(row,chk){
            if(!_chkField) return;// 未定义可选中
            var applyRow=function(r,rChk){
                var oldChk=!!r[_chkField];// 修改前的选中状态
                if(oldChk != rChk){
                    r[_chkField]=rChk;
                    var i=parseInt(_curData.indexOf(r));// 当前页中的索引
                    if(i!=-1){
                        var rowEl=el.find('tbody>tr[data-i="'+i+'"]');
                        rChk ? rowEl.addClass('trchk') : rowEl.removeClass('trchk');
                        !opts.singleChk && rowEl.find('>td[data-name="'+_chkField+'"]>input').prop('checked',rChk);
                    }
                    _cb && _cb.onCheckRow && rChk && _cb.onCheckRow(r,i,rowEl);// 选中回调
                    _cb && _cb.onUnCheckRow && !rChk && _cb.onUnCheckRow(r,i,rowEl);// 取消选中回调
                }
            };
            var chkStats=!!chk,i;
            // 单选情况下选中，先取消其他行选中
            if(opts.singleChk && chkStats==true){
                var idxs=_getCheckedIdx();
                for(i=0;i<idxs.length;i++){
                    applyRow(_curData[idxs[i]],false);
                }
            }
            if(Object.prototype.toString.call(row) === '[object Array]'){// 修改数组中的行的选中状态
                for(i=0;i<row.length;i++){
                    applyRow(row[i],chkStats);
                }
            }else{
                applyRow(row,chkStats);
            }
        };
        /**
         * 根据请求获取的数据绘制表格内容
         * @param {object} datalist 数据列表
         * @param {object|undefined=} pageObj 分页对象
         * @param {object|Array=} data 后台分页时，每次分页请求到的后台数据的 data 部分;前台分页时，所有数据的集合
         * @private
         */
        var _drawFn=function(datalist,pageObj,data){
            var startNo=1;
            _curData=datalist;
            _pageObj=pageObj;
            if(pageObj) startNo=(_pageObj.curpage-1)*_pageObj.percount+1 || 1;// 根据分页对象获得第一条的序号
            var dataStr='';
            if(datalist && datalist.length>0){
                for(var i=0;i<datalist.length;i++){
                    var data=datalist[i];
                    var rowCls='';
                    if(opts.rowClser){
                        rowCls=opts.rowClser(data,i) || '';
                    }
                    if(_chkField && data[_chkField]){// 当前行选中
                        dataStr+='<tr data-i="'+i+'" class="trchk '+rowCls+'">';
                    }else{
                        dataStr+='<tr data-i="'+i+'" class="'+rowCls+'">';
                    }
                    if(opts.showno){
                        dataStr+='<td class="ctrltd">'+(startNo+i)+'</td>';
                    }
                    if(_chkField && !opts.singleChk){
                        dataStr+='<td style="text-align:center;" data-name="'+_chkField+'"><input type="checkbox"'+(data[_chkField] ? ' checked':'')+' /></td>';
                    }
                    for(var j= 0,col,cssStr;j<_cols.length;j++){
                        col=_cols[j];
                        cssStr='';
                        if(col.align) cssStr+='text-align:center;';// 对齐方式
                        if(cssStr) cssStr=' style="'+cssStr+'"';// 需要定义 style
                        dataStr+='<td data-name="'+col.name+'"'+cssStr+(col.showTip ? ' title="'+(data[col.field]||'')+'"' : '')+'>'
                            +(col.render ? col.render(data[col.field],data,i,_returnObj) : data[col.field]||'')
                            +'</td>';
                    }
                    dataStr+='</tr>';
                }
            }else if(opts.prompt!=false){
                var colspan=_cols.length;
                opts.showno && colspan++;
                _chkField && !opts.singleChk && colspan++;
                dataStr='<tr><td class="nodata" colspan="'+colspan+'">未查询到符合条件的数据!</td></tr>'
            }
            el.find('tbody').html(dataStr);
            if(opts.url){// 后台分页/后台不分页，当前数据就是全部可操作数据，前台分页/不分页在设置数据源时记录
                _data=datalist;
            }
            _cb && _cb.afterShowData && _cb.afterShowData(_returnObj);
            if(opts.url){
                _filterObj && !_filterObj.getData() && _filterObj.setData(_data);
            }
        };
        /**
         * 获取当前列表所有选中行索引
         * @return {Array}
         * @private
         */
        var _getCheckedIdx=function(){
            var sels=[];
            if(_chkField){
                for(var i=0;i<_data.length;i++){
                    if(_data[i][_chkField]) sels.push(i);
                }
            }
            return sels;
        };
        var _initBindEvent=function(){
            if(_cb){
                var name,eType;// 临时变量
                // 行事件
                var cbRow=_cb.row;
                if(typeof cbRow=='object'){
                    var cbRowHandle=function(handle){
                        return function(e){
                            var el=$(this)
                                ,i=parseInt(el.attr('data-i'));
                            handle.call(this,e,_curData[i],i);
                        };
                    };
                    for(eType in cbRow){
                        if(cbRow.hasOwnProperty(eType))
                            el.on(eType+'.row','tbody>tr',cbRowHandle(cbRow[eType]));
                    }
                }
                // 单元格事件
                var cbCell=_cb.cell;
                if(typeof cbCell=='object'){
                    var cbCellHandle=function(handle,name){
                        var col=_findCol('name',name)[0];
                        return function(e){
                            var el=$(this)
                                ,i=parseInt(el.parent().attr('data-i'));
                            handle.call(this,e,_curData[i][col.field],_curData[i],i);
                        };
                    };
                    for(name in cbCell){
                        if(cbCell.hasOwnProperty(name))
                            for(eType in cbCell[name]){
                                if(cbCell[name].hasOwnProperty(eType)){
                                    el.on(eType+'.cell_'+name,'tbody>*>td[data-name="'+name+'"]',cbCellHandle(cbCell[name][eType],name));
                                }
                            }
                    }
                }
            }
            // 默认行点击变色事件
            el.on('click._row','tbody>tr[data-i]',function(){
                var tr=$(this);
                if(tr.hasClass('now')) return;
                tr.siblings('.now').removeClass('now');
                tr.addClass('now');
            });
            // 行选中事件
            if(_chkField){
                if(opts.singleChk){// 单选
                    el.on('click.chk','tbody>tr[data-i]',function(){
                        var tr=$(this)
                            ,rowI=parseInt(tr.attr('data-i'))
                            ,stat=_curData[rowI][_chkField];
                        _checkRow(_curData[rowI], true);// 选中点击行
                    });
                }
                else{// 复选
                    el.on('click.chk','tbody>tr>td[data-name="'+_chkField+'"]',function(){
                        var td=$(this)
                            ,rowI=parseInt(td.parent().attr('data-i'))
                            ,stat=_curData[rowI][_chkField];
                        _checkRow(_curData[rowI], !stat);
                    });
                    el.on('click.chkAll','thead>tr>[data-name="'+_chkField+'"]',function(e){
                        var stat,input;
                        input=$(this).children('input');
                        if(e.target.tagName.toLocaleLowerCase()=='input'){// 事件源是 input
                            stat=input.prop('checked');
                        }else{
                            stat=!input.prop('checked');
                        }
                        input.prop('checked',stat);
                        _checkRow(_curData,stat);
                    });
                }
            }
        };
        /**
         * 绘制表格，确定表头，并完成事件绑定
         * @private
         */
        var _initTable=function(){
            // 未定义选中状态字段名，且允许单选或多选
            if(typeof _chkField != 'string' && (opts.singleChk || _chkField==true)){
                _chkField='__checked';
            }
            var colAlignArr=['center','left','right'];// col.align 合法值
            var tableStr='<table class="listtable" cellspacing="0" cellpadding="0">';
            if(opts.showHead){
                tableStr+='<thead><tr>';
                if(opts.showno){
                    tableStr+='<th style="width:50px;">&nbsp;</th>';
                }
                if(_chkField && !opts.singleChk){// 复选
                    tableStr+='<th style="width:30px;" data-name="'+_chkField+'"><input type="checkbox" /></th>';
                }
                for(var i= 0,col;i<_cols.length;i++){
                    col=_cols[i];
                    // name
                    if(!col.name) col.name=col.field;
                    // 水平对齐方式
                    if(col.align && colAlignArr.indexOf(col.align)!=-1){// 指定了合法的 align
                        if(col.align=='left') delete col.align;// 此列为默认对齐方式
                    }else if(col.type=='number'){// 未指定 align，且为数字，居右显示
                        col.align='right';
                    }else{
                        delete col.align;
                    }
                    var colStr=col.hrender ? col.hrender() : col.title;
                    tableStr+='<th style="'+(col.width ? 'width:'+col.width : '')+'" data-name="'+col.name+'">'+colStr+'</th>';
                }
                tableStr+='</tr></thead>';
            }
            tableStr+='<tbody></tbody>' +
                '</table>';
            el.append(tableStr);// 将表格添加到 DOM
            if(opts.filterEl){// 后台分页时不支持过滤
                var filter=opts.filter;
                filter.hide = opts.singleChk && filter.hide!=false ;// 只有单选且允许隐藏时为 true，多选禁止隐藏
                var filterOpts={
                    fields:filter.fields
                    ,afterFilter:function(newData,keyword){
                        if(opts.url && _page) return;// 后台分页时不支持过滤
                        if(opts.data && _page){// 前台分页
                            _manuObj && _manuObj.reset({
                                data:newData
                            })
                        }else{
                            _drawFn(newData);
                        }
                    }
                };
                if(filter.pySupport!=undefined) filterOpts.pySupport=filter.pySupport;
                _filterObj=opts.filterEl.initFilter(filterOpts);
            }
            /* 创建回调监听 */
            _initBindEvent();
        };
        /**
         * 请求数据（分页）并绘制到 DOM
         * @private
         */
        var _bindData=function(){
            _data=opts.data;
            if(_page) {// 前台分页/后台分页
                var manu = el.next('.manu');
                if (!manu.length) {
                    manu = $('<div class="manu"></div>');
                    el.after(manu);
                }
                _manuObj = manu.initPageBar({
                    per:_page.percount// 每页显示的数据条数
                    ,url:opts.url// 后台分页时请求的url
                    ,args:opts.args
                    ,data:_data
                    ,showloading:opts.showloading
                    ,perList:_page.perList// 可选择的每页页码列表，若未定义则不可切换
                    ,afterFlip:_drawFn// 每次翻页后的回调函数，datalist:当前页数据；pageObj:当前分页信息
                });
            }else if(!_data){// 后台不分页
                el.request({
                    drawDomFn:_drawFn
                    ,showloading:opts.showloading
                    ,ajaxOpts:{
                        url:opts.url
                        ,data:opts.args
                    }
                });
            }else{// 前台不分页
                _manuObj && _manuObj.destroy();
                _manuObj=undefined;
                _drawFn(_data);
            }
            if(_data && _filterObj){// 前台过滤
                _filterObj.setData(_data);
            }
        };
        /**
         * 合并指定单元格
         * @param merges {Array} 要合并的单元格信息
         *   merges[i].rowI {int} 要合并的单元格在当前页的索引
         *   merges[i].name {string} 要合并的单元格所在列对应的 name
         *   merges[i].rowspan {int} 合并几行
         *   merges[i].colspan {int} 合并几列
         * @private
         */
        var _merge=function(merges){
            if(!merges || !merges.length) return;
            var _adjustMerge=function(mergeItem){
                var rowI=mergeItem['rowI'],colI=-1
                    ,name=mergeItem['name']
                    ,rowspan=mergeItem['rowspan']
                    ,colspan=mergeItem['colspan'];
                var cellEl=el.find('tbody>tr[data-i="'+rowI+'"]>td[data-name="'+name+'"]')
                    ,rowEl;
                if(!cellEl) return;
                for(var i= 0,len=_cols.length;i<len;i++){// 获取当前列定义索引，由于判断过 cellEl，所以 colI 必有意义
                    if(name==_cols[i]['name']){
                        colI=i;
                        break;
                    }
                }
                rowspan && cellEl.attr('rowspan',rowspan);
                colspan && cellEl.attr('colspan',colspan);
                /* 调整受影响的单元格 */
                rowspan=parseInt(cellEl.attr('rowspan')) || 1;
                colspan=parseInt(cellEl.attr('colspan')) || 1;
                for(var j=0;j<rowspan;j++){
                    rowEl=el.find('tbody>tr[data-i="'+(rowI+j)+'"]');
                    for(var k=0;k<colspan;k++){
                        if(j==0 && k==0) continue;// 保留当前单元格
                        rowEl.find('>td[data-name="'+_cols[colI+k]['name']+'"]').remove();
                    }
                }
            };
            for(var i= 0,len=merges.length;i<len;i++){
                _adjustMerge(merges[i]);
            }
        };
        /**
         * 销毁 grid
         * @private
         */
        var _destroy=function(){
            if(!el || !el.children().length) return;
            /* 解绑事件监听 */
            if(_cb){
                var name,eType;// 临时变量
                // 行事件
                var cbRow=_cb.row;
                if(typeof cbRow=='object'){
                    for(eType in cbRow){
                        if(cbRow.hasOwnProperty(eType))
                            el.off(eType+'.row');
                    }
                }
                // 单元格事件
                var cbCell=_cb.cell;
                if(typeof cbCell=='object'){
                    for(name in cbCell){
                        if(cbCell.hasOwnProperty(name))
                            for(eType in cbCell[name]){
                                if(cbCell[name].hasOwnProperty(eType)){
                                    el.off(eType+'.cell_'+name);
                                }
                            }
                    }
                }
            }
            el.off('click.chk')
                .off('click.chkAll')
                .off('click._row');
            /* 解绑分页 */
            _manuObj && _manuObj.destroy();
            el.next('.manu').remove();
            // 清空 DOM
            el.html('');
        };
        _returnObj={
            el:el
            /**
             * 根据条件查找所有符合的 cols[i].key=val 的列
             */
            ,findCol:_findCol
            /**
             * 根据条件查找所有符合的 data[i].field=val 的行
             */
            ,findRow:_findRow
            /**
             * 获取当前列表所有选中行索引
             */
            ,getCheckedIdx:_getCheckedIdx
            /**
             * 获取当前列表所有选中行
             */
            ,getCheckedRows:function(){
                var chks=_getCheckedIdx();
                var rows=[];
                for(var i=0;i<chks.length;i++){
                    rows.push(_data[chks[i]]);
                }
                return rows;
            }
            /**
             * 返回当前显示的数据列表
             * @returns {object}
             */
            ,getData:function(){
                return $.extend([],_data);
            }
            ,getCurData:function(){
                return $.extend([],_curData);
            }
            /**
             * 改变指定行的选中状态
             * @param row {object} 要选中的行对应的数据
             * @param chk {bool} true 时改为选中，false 改为不选中
             */
            ,checkRow:function(row,chk){
                _checkRow(row,chk);
            }
            /**
             * 重置当前显示的数据列表
             * @param {object} data 新的列表数据
             */
            ,setData:function(data){
                _drawFn(data);
            }
            /**
             * 返回当前的分页信息
             * @returns {object}
             */
            ,getPage:function(){
                return _pageObj;
            }
            ,getPageObj:function(){
                return _manuObj;
            }
            /**
             * 扩展初始化时的配置，重新请求数据（分页），不重绘整个 Grid
             * @param cusOpts {object=} 若不定义则直接以原始配置请求数据
             *  - cusOpts.url {string=} 请求路径
             *  - cusOpts.args {object=} 请求参数的键值对
             *  - cusOpts.page {object=} 分页数据
             *      cusOpts.page.percount {int} 默认每页数据条数
             *      cusOpts.page.perList {Array} 分页数据列表
             */
            ,resetData:function(cusOpts){
                if(cusOpts){
                    if(cusOpts.url){
                        opts.url=cusOpts.url;
                        opts.data=undefined;
                    }else if(cusOpts.data){
                        opts.data=cusOpts.data;
                        opts.url=undefined;
                    }
                    if(cusOpts.args) opts.args=$.extend(opts.args,cusOpts.args);
                    if(cusOpts.page==false){
                        _page=false;
                    }else if(cusOpts.page){
                        _page=$.extend(_page,cusOpts.page);
                    }
                }
                _bindData();
            }
            ,merge: _merge
            /**
             * 销毁 grid
             */
            ,destroy:_destroy
        };
        _destroy();
        _initTable();
        _bindData();
        return _returnObj;
    };
    /**
     * 将 .tab-box 元素初始化为 tab 容器
     * @param _opts {object=}
     *   - opts.tabs {Array}
     *       opts.tabs[i].val {string} tab 页标识
     *       opts.tabs[i].txt {string=} tab 页标题，默认与 val 一致
     *       opts.tabs[i].remove {bool=} 是否可关闭，默认为 false
     *       opts.tabs[i].width {string=} 头部宽度，默认 '120px'，scroll=false 且 tab 页显示超出范围，会引起此属性失效
     *       opts.tabs[i].cont {string=} 对应内容部分 DOM 字符串
     *       opts.tabs[i].url {string=} 对应内容部分 url
     *   - opts.headAlign {string=} 若设为 ‘right’ 则将 tab 头部居右显示，，此时 opts.scroll 失效，不可滚动
     *   - opts.context {bool=} 是否显示右键菜单，默认为 false
     *   - opts.scroll 是否支持头部超出滚动，默认为 false
     *   - opts.noAdjust 是否禁止监听容器尺寸变化以自动调整尺寸，默认为 false，容器尺寸不会变化时，可设为 true 以提高效率
     *   - opts.showAdd {bool=} 是否显示添加按钮，默认为 false
     *   - opts.addHandler() {function=} 添加按钮的事件
     *   - opts.onChange(val) {function=} 切换tab时触发事件，参数为要选中的 tab.val
     *   - opts.onRemove(val) {function=} 删除tab时触发事件，参数为要删除的 tab.val
     * @return {object|undefined} obj
     *   - obj.refreshTab() 刷新指定val的tab页，若是由cont设置的内容，则此方法无作用
     *   - obj.addTab(tab,noToggle)
     *       tab {object} tab 页对象，结构同 opts.head
     *       noToggle {bool=} 添加后是否禁止切换，默认为 false，即，添加即切换
     *   - obj.getTabs(key,value) 选中所有符合条件的 tab 列表，无条件则选择全部，未找到则返回 []
     *       key {string} tab 页对象中的键：val,txt,remove
     *       value {string=}
     *   - obj.removeTab(tab) 删除指定的 tab
     *   - obj.selectTab(tab) 切换到指定的 tab
     *   - obj.adjust() 手动计算绘制头部尺寸
     *   - obj.getContEl(tab) 获取内容部分的 jquery 元素对象
     */
    $.fn.initTabsBox=function(_opts){
        var el=$(this);
        if(!el.hasClass('tab-box')) return;
        var CONF={
            scrollCtrlW:20// 左右滚动按钮宽度各为 20
            ,addW:30
            ,liBefore:0
        };
        var _returnObj;
        var contBoxEl=el.children('.tab-cont-box');
        var headBoxEl=el.children('.tab-head-box');
        var opts=$.extend({
            tabs:[]
//            ,headAlign:'right'
            ,context:false
            ,scroll:false
            ,noAdjust:false
            ,showAdd:false
        },_opts);
        var _tabs=[];
        var tabGuid = opts.noAdjust ? undefined : $.generateGuid('tab');// 用于标识 window 的 resize 事件

        var _destroy=function(){
            if(headBoxEl.length){
                _tabs.length=0;
                tabGuid && $(window).off('resize.tab_'+tabGuid);
                headBoxEl.off('click.changeTab')
                    .off('click.addTab')
                    .off('click.scroll');
                headBoxEl.remove();
            }
        };
        var tabSizes=[];// 支持滚动的状态记录 tab 头部尺寸
        var tabTotalSize=0;
        var ulWidth=0;// ul 可显示的尺寸
        /**
         * 重新计算 tab 头部尺寸
         * @param index {int=} 要重计算的 tab 的索引，未指定则重计算全部 tab
         * @private
         */
        var _calulateSizes=function(index){
            var lis=headBoxEl.find('.tab-head')
                ,li,liBCR,item,curW;
            if(index==undefined){// 重新计算全部
                tabSizes.length=0;
                tabTotalSize=0;
                var len=_tabs.length;
                for(index=0;index<len;index++){
                    item=_tabs[index];
                    li=lis.eq(index);
                    li.css('width',item['width']||'');
                    liBCR=li[0].getBoundingClientRect();
                    curW=liBCR.right-liBCR.left;
                    tabSizes.push(curW);
                    tabTotalSize+=curW;
                }
            }else{
                item=_tabs[index];
                li=lis.eq(index);
                li.css('width',item['width']||'');
                liBCR=li[0].getBoundingClientRect();
                curW=tabSizes[index];
                if(curW!=undefined) tabTotalSize-=curW;
                curW=liBCR.right-liBCR.left;
                tabTotalSize+=curW;
                tabSizes[index]=curW;
            }
        };
        /**
         * 根据指定项或选中项滚动
         * @param index {int=} 要滚动到视图内的 tab 索引，未定义则为当前选中项索引
         * @private
         */
        var _scrollToSeL=function(index){
            if(!headBoxEl.hasClass('tab-scroll')) return;
            var headUl=headBoxEl.find('>ul')
                ,lis=headUl.children('.tab-head');
            var curLeft=0,curRight=0;// 选中项边界
            if(index==undefined) index=lis.index(lis.filter('.now'));
            for(var i= 0;i<tabSizes.length;i++){
                if(i==index){// 选中项
                    curRight=curLeft+tabSizes[i];
                    break;
                }
                curLeft+=tabSizes[i];
            }
            if(headUl.is(':animated')) headUl.stop(true);
            if(curRight==0){
//                headUl.css({'left':CONF.scrollCtrlW+'px'});
                headUl.animate({'left':CONF.scrollCtrlW+'px'});
            }else{
                var hideL=CONF.scrollCtrlW-headUl.position().left;// 左侧已被隐藏的部分的尺寸
                if(hideL>curLeft){
//                    headUl.css({'left':-curLeft+CONF.scrollCtrlW+'px'});
                    headUl.animate({'left':-curLeft+CONF.scrollCtrlW+'px'});
                }else if(ulWidth+hideL-CONF.scrollCtrlW<curRight){
//                    headUl.css({'left':ulWidth-curRight-CONF.scrollCtrlW+'px'});
                    headUl.animate({'left':ulWidth-curRight-CONF.scrollCtrlW+'px'});
                }
            }
        };
        /**
         * 滚动头部
         * @param toNext {bool=} 向后滚动
         * @private
         */
        var _scrollHead=function(toNext){
            if(!headBoxEl.hasClass('tab-scroll')) return;
            var headUl=headBoxEl.find('>ul');
            var hideL=CONF.scrollCtrlW-headUl.position().left// 左侧已被隐藏的部分的尺寸
                ,hideR=hideL+ulWidth-CONF.scrollCtrlW;// 右侧被隐藏部分前的尺寸
            var curW= 0, i, index;
            if(toNext){// 向后
                for(i= 0;i<tabSizes.length;i++){
                    curW+=tabSizes[i];
                    if(curW>hideR){
                        index = i;
                        break;
                    }
                }
            }else{// 向前
                for(i= 0;i<tabSizes.length;i++){
                    curW+=tabSizes[i];
                    if(curW>=hideL){
                        index = i;
                        break;
                    }
                }
            }
            if(index!=undefined) _scrollToSeL(index);
        };
        var _adjust=function(){
            var headUl=headBoxEl.find('>ul');
            _calulateSizes();// 复原尺寸
            // 计算 ul 尺寸
            var headBCR=headBoxEl.eq(0)[0].getBoundingClientRect();
            ulWidth=headBCR.right-headBCR.left;
            if(opts.showAdd){// 显示添加
                ulWidth-=CONF.addW;
            }
            if(tabTotalSize!=0 && ulWidth!=0 && tabTotalSize>ulWidth){// 超出
                if(opts.scroll){
                    headBoxEl.addClass('tab-scroll');
                    headUl.css('width',tabTotalSize+'px');
                    var left=headUl.position().left;
                    if(left > CONF.scrollCtrlW){// 左侧有留白
                        headUl.css({'left':CONF.scrollCtrlW+'px'});
                    }else if(tabTotalSize + left < ulWidth - CONF.scrollCtrlW){// 右侧有留白
                        headUl.css({'left':ulWidth - tabTotalSize - CONF.scrollCtrlW+'px'});
                    }
                }else{
                    tabSizes.length && headUl.children('.tab-head').css('width',100/tabSizes.length+'%');
                }
            }else{
                if(opts.scroll){
                    if(headUl.is(':animated')) headUl.stop(true);
                    headUl.css('width','');
                    headBoxEl.removeClass('tab-scroll');
                    headUl.css('left',0);
                }
            }
        };
        /**
         * 添加 tab 页
         * @param tab {object} tab 页对象，结构同 opts.tabs[i]
         * @param noToggle {bool=} 添加后是否不立即切换，默认为 false，即，添加即切换
         * @private
         */
        var _addTab=function(tab,noToggle){
            if(!headBoxEl || !tab) return;
            var val=tab.val;
            var sameTab=_getTab('val',val);
            if(sameTab){
                _selectTab(sameTab);// 选中已有项
            }else{
                !tab.txt && (tab.txt=tab.val);
                var txt=tab.txt
                    ,remove=tab.remove || false;
                var cssStr='';
                if(remove){
                    cssStr+='padding-right:20px;';
                }
                if(tab.width) cssStr+='width:'+tab.width+';';
                var li=$('<li title="'+txt+'" class="tab-head" data-val="'+val+'" style="'+ cssStr +'">'
                    +txt
                    +(remove?'<span class="fa fa-remove"></span>':'')
                    +'</li>');
                if(opts.headAlign=='right'){
                    headBoxEl.children('ul').prepend(li);
                }else{
                    headBoxEl.children('ul').append(li);
                }
                /* 内容部分 */
                var contStr=tab.url ?
                    '<iframe width="100%" height="100%" data-name="'+tab.val+'" frameborder="0" scrolling="0" src="'+tab.url+'"></iframe>':
                    tab.cont;
                var contEl=contBoxEl.find('>.tab-cont[data-val="'+tab.val+'"]');
                if(contStr){
                    if(contEl.length){
                        contEl.html(contStr);
                    }else{
                        contBoxEl.append('<div class="tab-cont" data-val="'+val+'">'+contStr+'</div>');
                    }
                }
                _tabs.push(tab);
                _calulateSizes(_tabs.length-1);
                _adjust();
                !noToggle && headBoxEl.find('>ul>li.tab-head[data-val="'+val+'"]').click();
            }
        };
        var _getTabs=function(key,value){
            var tabs=[], i, tab;
            if(key){
                for(i= 0;i<_tabs.length;i++){
                    tab=_tabs[i];
                    if(tab[key]==value){
                        tabs.push(tab);
                    }
                }
            }else{
                $.extend(tabs,_tabs);
            }
            return tabs;
        };
        var _getTab=function(key,value){
            var i, tab;
            if(key){
                for(i= 0;i<_tabs.length;i++){
                    tab=_tabs[i];
                    if(tab[key]==value){
                        return tab;
                    }
                }
            }
        };
        var _refreshTab=function(val){
            var contFrame=contBoxEl.find('>.tab-cont[data-val="'+val+'"]>iframe');
            if(contFrame.length) contFrame[0].contentWindow.location.reload();
        };
        var _selectTab=function(tab,val){
            if(typeof tab=='string' && val!=undefined){// 根据键值查找 tab
                tab=_getTab(tab,val);
                if(!tab) return;
            }
            if(!tab) return;
            var headEl=headBoxEl.find('>ul>li.tab-head[data-val="'+tab.val+'"]');
            if(!headEl.hasClass('now')){// tab 未选中
                headBoxEl.find('>ul>li.tab-head.now').removeClass('now');
                contBoxEl.find('>.tab-cont.now').removeClass('now');
                headEl.addClass('now');
                contBoxEl.find('>.tab-cont[data-val="'+tab.val+'"]').addClass('now');
                opts.onChange && opts.onChange(tab.val);
            }
            _scrollToSeL();
        };
        /**
         * 删除指定的 tab
         * @param tab {object|string}
         * @param val {string=}
         * @private
         */
        var _removeTab=function(tab,val){
            if(typeof tab=='string' && val!=undefined){// 根据键值查找 tab
                tab=_getTab(tab,val);
                if(!tab) return;
            }
            var headEl=headBoxEl.find('>ul>li.tab-head[data-val="'+tab.val+'"]');
            if(headEl.hasClass('now') && _tabs.length>1){// 当前项为选中项，且还有其他标签页
                var i=_tabs.indexOf(tab);
                if(i!=_tabs.length-1){// 不是最后一项，选中后一项
                    _selectTab(_tabs[i+1]);
                }else{
                    _selectTab(_tabs[i-1]);
                }
            }
            var index=_tabs.indexOf(tab);
            _tabs.splice(index,1);// 移除
            tabSizes.splice(index,1);// 移除尺寸记录
            headEl.remove();// 移除标题
            contBoxEl.find('>.tab-cont[data-val="'+tab.val+'"]').remove();
            opts.onRemove && opts.onRemove(tab.val);
            _adjust();
        };
        /**
         * 删除全部 tab
         * @private
         */
        var _removeAll=function(){
            for(var i=_tabs.length-1;i>=0;i--){
                _removeTab(_tabs[i]);
            }
        };
        var _init=function(){
            if(!headBoxEl.length){
                headBoxEl=$('<div class="tab-head-box"><ul></ul>' +
                    (opts.scroll?'<div class="tab-prev"></div><div class="tab-next"></div>':'') +
                    '</div>');
            }
            if(!contBoxEl.length){
                contBoxEl=$('<div class="tab-cont-box"></div>');
            }
            headBoxEl
                .on('click.changeTab','>ul>li.tab-head',function(){
                    var head=$(this);
                    var val=head.attr('data-val');
                    _selectTab('val',val);
                })
                .on('click.removeTab','>ul>li.tab-head>.fa-remove',function(e){
                    var tab=_getTab('val',$(this).parent().attr('data-val'));
                    _removeTab(tab);
                    e.stopPropagation();
                });
            tabGuid && $(window).on('resize.tab_'+tabGuid,_adjust);// 监听窗口 resize 事件
            el.prepend(headBoxEl);// 添加 head-box
            el.append(contBoxEl);// 添加 cont-box
            // 居右显示
            if(opts.headAlign=='right'){
                opts.scroll=false;// TODO 居右时暂不支持滚动
                headBoxEl.children('ul').addClass('tab-heads-r');
            }
            /* 根据 opts.tabs，添加 tab 页 */
            for(var i= 0;i<opts.tabs.length;i++){
                _addTab(opts.tabs[i],true);
            }
            // 显示添加
            if(opts.showAdd){
                headBoxEl.addClass('tab-withAdd');
                headBoxEl.append('<div class="tab-add" title="新增"><span class="fa fa-plus-circle"></span></div>');
                opts.addHandler && headBoxEl.on('click.addTab','>.tab-add',function(){
                    opts.addHandler();
                })
            }
            if(opts.scroll){
                headBoxEl.on('click.scroll','.tab-prev,.tab-next',function(){
                    _scrollHead($(this).hasClass('tab-next'));
                });
            }
            headBoxEl.find('li.tab-head').eq(0).click();// 默认选中第一项
        };
        _returnObj={
            refreshTab: _refreshTab
            ,addTab: _addTab
            ,getTabs: _getTabs
            ,getTab: _getTab
            ,removeTab: _removeTab
            ,removeAll: _removeAll
            ,selectTab: _selectTab
            ,adjust:_adjust
            ,getSelect: function(){
                return _getTab('val',headBoxEl.find('>ul>li.now').attr('data-val'));
            }
            ,getContEl: function(tab){
                return contBoxEl.find('>.tab-cont[data-val="'+tab.val+'"]');
            }
        };
        _destroy();
        _init();
        return _returnObj;
    };
    /**
     * 将 .tab-box 元素初始化为 tab 容器
     * @param _opts {object=}
     *   - opts.tabs {Array}
     *       opts.tabs[i].val {string} tab 页标识
     *       opts.tabs[i].txt {string=} tab 页标题，默认与 val 一致
     *       opts.tabs[i].remove {bool=} 是否可关闭，默认为 false
     *       opts.tabs[i].width {string=} 头部宽度，默认 '120px'，scroll=false 且 tab 页显示超出范围，会引起此属性失效
     *       opts.tabs[i].cont {string=} 对应内容部分 DOM 字符串
     *       opts.tabs[i].url {string=} 对应内容部分 url
     *   - opts.headAlign {string=} 若设为 ‘right’ 则将 tab 头部居右显示
     *   - opts.scroll 是否支持头部超出滚动，默认为 true
     *   - opts.noAdjust 是否禁止监听容器尺寸变化以自动调整尺寸，默认为 false，容器尺寸不会变化时，可设为 true 以提高效率
     *   - opts.showAdd {bool=} 是否显示添加按钮，默认为 false
     *   - opts.addHandler() {function=} 添加按钮的事件
     *   - opts.onChange(val) {function=} 切换tab时触发事件，参数为要选中的 tab.val
     *   - opts.onRemove(val) {function=} 删除tab时触发事件，参数为要删除的 tab.val
     * @return {object|undefined} obj
     *   - obj.addTab(tab,noToggle)
     *     tab {object} tab 页对象，结构同 opts.head
     *     noToggle {bool=} 添加后是否禁止切换，默认为 false，即，添加即切换
     *   - obj.getTabs(key,value) 选中所有符合条件的 tab 列表，无条件则选择全部，未找到则返回 []
     *     key {string} tab 页对象中的键：val,txt,remove
     *     value {string=}
     *   - obj.removeTab(tab) 删除指定的 tab
     *   - obj.selectTab(tab) 切换到指定的 tab
     *   - obj.adjust() 手动计算绘制头部尺寸
     *   - obj.getContEl(tab) 获取内容部分的 jquery 元素对象
     */
    $.fn.initScrollTabBox=function(_opts){
        var el=$(this);
        return el.initTabsBox($.extend({
            scroll:true
            ,context:true
        },_opts));
    };

    $.fn.initTabBox=function(_opts){
        var el=$(this);
        if(!el.hasClass('tab-box')) return;
        var _returnObj;
        var opts={};
        if(_opts.head){
            opts.tabs=_opts.head;
            delete _opts.head;
        }
        $.extend(opts,_opts);
        _returnObj=el.initTabsBox(opts);
        var _addTab=_returnObj.addTab;
        _returnObj.addTab=function(tab,cont,noToggle){
            if(cont) tab.cont=cont;
            _addTab(tab,noToggle);
        };
        return _returnObj;
    };

    /**
     * 弹出位置计算
     * @param elBCR {object} 参照元素的 BCR
     * @param targetElWidth {int} 弹出项的宽
     * @param targetElHeight {int} 弹出项的高
     * @param viewW {int} 视图范围的宽
     * @param viewH {int} 视图范围的高
     * @param positionStr {string} 定位方向: 'p-p'，可能的值：top,left,right,bottom,center。
     * @param adjust {bool=} 是否允许调整弹出方向
     * @param appendToEl {object=} 以参照元素计算定位，未定义则以窗口为参照(.overlay)
     */
    $.adaptElement=function(elBCR, viewW, viewH, targetElWidth, targetElHeight, positionStr, adjust, appendToEl){
        var elTop=elBCR.top
            ,elLeft=elBCR.left
            ,elBot=elBCR.bottom
            ,elRight=elBCR.right;
        var positionStrParts = typeof positionStr=='string' ? positionStr.split('-') : [];
        var pos0= positionStrParts[0] || 'bottom'
            ,pos1= positionStrParts[1] || 'left';
        var cssObj={};
        if(adjust) {// 允许调整方向
            /**
             * 返回是否需要反向
             * @param nowS - 当前空间尺寸
             * @param otherS - 备选空间尺寸
             * @param targetS - 需要的空间尺寸
             */
            var shouldChange = function (nowS, otherS, targetS) {
                return nowS < targetS && otherS >= targetS;//当前空间不足，反向空间足够，返回 true，即需要反向
            };
            // 确定 pos0，若当前空间不足且备选空间足够，或都不足但备选空间较大，则反向
            switch (pos0) {
                case 'left':
                    shouldChange(elLeft, viewW - elRight, targetElWidth) && (pos0 = 'right');
                    break;
                case 'right':
                    shouldChange(viewW - elRight, elLeft, targetElWidth) && (pos0 = 'left');
                    break;
                case 'top':
                    shouldChange(elTop, viewH - elBot, targetElHeight) && (pos0 = 'bottom');
                    break;
                default :
                    pos0 = 'bottom';
                    shouldChange(viewH - elBot, elTop, targetElHeight) && (pos0 = 'top');
            }
            // 确定 pos1
            switch (pos1) {
                case 'center':
                    break;
                case 'top' :
                    shouldChange(viewH - elTop, elBot, targetElHeight) && (pos1 = 'bottom');
                    break;
                case 'bottom':
                    shouldChange(elBot, viewH - elTop, targetElHeight) && (pos1 = 'top');
                    break;
                case 'right':
                    shouldChange(elRight, viewW - elLeft, targetElWidth) && (pos1 = 'left');
                    break;
                default :
                    pos1 = 'left';
                    shouldChange(viewW - elLeft, elRight, targetElWidth) && (pos1 = 'right');
            }
        }
        if(appendToEl){
            // 一级位置已确定，通过返回方向由 class 名控制，不需计算
            // 二级方向位置确定，单向空间不足时，向右/下贴边
            switch(pos1){
                case 'center':
                    if(['left', 'right'].indexOf(pos0) >= 0){
                        cssObj.top = Math.floor((elBot-elTop - targetElHeight)/2) + 'px';
                    }
                    else{
                        cssObj.left = Math.floor((elRight-elLeft - targetElWidth)/2) + 'px';
                    }
                    break;
                case 'top' :
                    if(adjust != false && viewH - elTop < targetElHeight)
                        cssObj.top = viewH - elTop - targetElHeight + 'px';
                    break;
                case 'bottom':
                    if(adjust != false && elBot < targetElHeight)
                        cssObj.bottom = elBot - viewH + 'px';
                    break;
                case 'right':
                    if(adjust != false && elRight < targetElWidth)
                        cssObj.right = elRight - viewW + 'px';
                    break;
                default:
                    pos1 = 'left';
                    if(adjust != false && viewW - elLeft < targetElWidth)
                        cssObj.left = viewW - elLeft - targetElWidth + 'px';
            }
        }else{
            // 根据参照元素的文档位置，计算弹出项的文档位置
            switch(pos0){
                case 'left':
                    cssObj.left = elLeft - targetElWidth + 'px';
                    break;
                case 'right':
                    cssObj.left = elRight + 'px';
                    break;
                case 'top':
                    cssObj.top = elTop - targetElHeight + 'px';
                    break;
                default :
                    pos0 = 'bottom';
                    cssObj.top = elBot + 'px';
            }
            // 二级方向位置确定，单向空间不足时，向右/下贴边
            switch(pos1){
                case 'center':
                    if(['left', 'right'].indexOf(pos0) >= 0){
                        cssObj.top = elTop + Math.floor((elBot - elTop - targetElHeight)/2) + 'px';
                    }
                    else{
                        cssObj.left = elLeft + Math.floor((elRight - elLeft - targetElWidth)/2) + 'px';
                    }
                    break;
                case 'top':
                    if(adjust && viewH - elTop < targetElHeight)
                        cssObj.top = viewH - targetElHeight + 'px';// 贴边
                    else
                        cssObj.top = elTop + 'px';
                    break;
                case 'bottom':
                    if(adjust && elBot < targetElHeight)
                        cssObj.top = viewH - targetElHeight + 'px';// 贴边
                    else
                        cssObj.top = elBot - targetElHeight + 'px';
                    break;
                case 'right':
                    if(adjust && elRight < targetElWidth)
                        cssObj.left = elLeft - elLeft + viewW - targetElWidth + 'px';
                    else
                        cssObj.left = elRight - targetElWidth + 'px';
                    break;
                default :
                    pos1 = 'left';
                    if(adjust && viewW - elLeft < targetElWidth)
                        cssObj.left = elLeft - elLeft + viewW - targetElWidth + 'px';
                    else
                        cssObj.left = elLeft + 'px';
            }
        }
        return [{
            'top': cssObj.top ? cssObj.top : ''
            ,'bottom': cssObj.bottom ? cssObj.bottom : ''
            ,'left': cssObj.left ? cssObj.left : ''
            ,'right': cssObj.right ? cssObj.right : ''
        }, pos0 + '-' + pos1];
    };

    /**
     * 根据源对象深度拷贝数据，并删除 __ 开头的组件属性
     * @param src 拷贝参照的对象
     * @returns {*}
     */
    $.pureClone=function(src){
        // 基本类型及 function
        if(typeof src !== 'object' || src===null) return src;
        var dst = Object.prototype.toString.call(src) === '[object Array]' ? [] : {};//判断参数的类型,定义要拷贝的对象的数据类型
        for(var i in src){
            if(src.hasOwnProperty(i) && !/^__/.test(i)){
                dst[i] = typeof src[i] === 'object' && src ? $.pureClone(src[i]) : src[i];
            }
        }
        return dst;
    };
    $.generateGuid=function(key){
        return key + (new Date()).getTime();
    };
    /**
     * 初始化 numbox
     * @param option
     *   - option.maxVal {int} 控件支持的最大值 TODO 小数的支持
     *   - option.minVal {int} 控件支持的最小值 TODO 小数的支持
     *   - option.name {string} 控件中文本框的 name 值
     *   - option.value {int} 初始化时的值
     *   - option.enable {int} 初始化时是否启用，默认为 true
     *   - option.onChange {function(val)} 值变化时的回调方法
     * @return {object} returnObj
     *   - obj.setVal(val) 为文本框赋值（val {int}）
     *   - obj.setEnable(enable) 禁用/启用（enable {bool}）
     */
    $.fn.initSelNum=function(option){
        var $this = this;
        var settings = {
            maxVal:0 //最大值
            ,minVal:0 //最小值
            ,name:'' //其中文本框对应的 name
            ,enable:true
//                ,value //初始化时的值，默认为最小值
//                ,step:1 //TODO 每次上下增减的数值
        };
        option = $.extend(settings,option||{});
        var iCurVal;//保存当前值
        var returnObj={};
        returnObj.setVal=_setVal;
        returnObj.getVal=_getVal;
        returnObj.setEnable=_setEnable;

        // 初始化方法
        function _init(){
            $this.addClass('txt numbox');
            var str = '<input type="text" class="iptVal"'+(option.name ? ' name="'+option.name+'"' : '')+'/>' +
                '<div class="arrowWrap"><span class="arrowUp"></span><span class="arrowDown"></span></div>';
            $this.append(str);
            $this.find('.arrowUp,.arrowDown').on('click',clickFn);
            $this.find('.iptVal').on({
                keydown:keydownFn,
                change:changeFn
            });
            _setVal(option.value);// 赋初始值
            _setEnable(option.enable);
        }
        function _setEnable(enable){
            option.enable=enable;
            if(enable){// 启用
                $this.removeClass('disabled');
            }else{
                $this.addClass('disabled');
            }
        }
        function _getVal(){
            return iCurVal;
        }
        // 为文本框赋值（先判断值是否合法）
        function _setVal(val){
            if(isNaN(val) || val<option.minVal){
                val = option.minVal;
            }else if(val > option.maxVal){
                val = option.maxVal;
            }
            if(val != parseInt($this.find('.iptVal').val())){
                $this.find('.iptVal').val(val);
            }
            if(val != iCurVal){// 值发生变化
                iCurVal = val;
                option.onChange && option.onChange(parseInt(iCurVal));
            }
        }
        //上下箭头点击函数
        function clickFn(event){
            if(!option.enable) return;
            _setVal( $(this).hasClass('arrowUp') ? iCurVal+1 : iCurVal-1);
        }
        // 键盘抬起事件 - 将当前文本框内容值更新到组件
        function changeFn(event){
            if(!option.enable) return;
            _setVal( parseInt($this.find('.iptVal').val()));
        }
        //键盘按下事件
        function keydownFn(event){
            if(!option.enable) return;
            var code = event.which;
            if (code == 38) {// 加1
                _setVal(iCurVal+1);
            } else if (code == 40) {// 减1
                _setVal(iCurVal-1);
            }else if(!(code >= 96 && code <= 105
                || code >= 48 && code <= 57
                || code == 37 || code ==39 // 左右键
                || code == 8 // 退格
                /* ||code == 110 ||  code == 190小数点*/)){// TODO 小数点、负数
                return false;
            }
            // 数字按键引起的数据变化在 keyup 中执行
        }
        /* 初始化开始 */
        _init();
        return returnObj;
    };
    /**
     * 根据 zhStr 的拼音首字母是否包含 keyword
     * @param zhStr {string} 中文字符串
     */
    $.makePyArr=function(zhStr){
        var strChineseFirstPY = "YDYQSXMWZSSXJBYMGCCZQPSSQBYCDSCDQLDYLYBSSJGYZZJJFKCCLZDHWDWZJLJPFYYNWJJTMYHZWZHFLZPPQHGSCYYYNJQYXXGJHHSDSJNKKTMOMLCRXYPSNQSECCQZGGLLYJLMYZZSECYKYYHQWJSSGGYXYZYJWWKDJHYCHMYXJTLXJYQBYXZLDWRDJRWYSRLDZJPCBZJJBRCFTLECZSTZFXXZHTRQHYBDLYCZSSYMMRFMYQZPWWJJYFCRWFDFZQPYDDWYXKYJAWJFFXYPSFTZYHHYZYSWCJYXSCLCXXWZZXNBGNNXBXLZSZSBSGPYSYZDHMDZBQBZCWDZZYYTZHBTSYYBZGNTNXQYWQSKBPHHLXGYBFMJEBJHHGQTJCYSXSTKZHLYCKGLYSMZXYALMELDCCXGZYRJXSDLTYZCQKCNNJWHJTZZCQLJSTSTBNXBTYXCEQXGKWJYFLZQLYHYXSPSFXLMPBYSXXXYDJCZYLLLSJXFHJXPJBTFFYABYXBHZZBJYZLWLCZGGBTSSMDTJZXPTHYQTGLJSCQFZKJZJQNLZWLSLHDZBWJNCJZYZSQQYCQYRZCJJWYBRTWPYFTWEXCSKDZCTBZHYZZYYJXZCFFZZMJYXXSDZZOTTBZLQWFCKSZSXFYRLNYJMBDTHJXSQQCCSBXYYTSYFBXDZTGBCNSLCYZZPSAZYZZSCJCSHZQYDXLBPJLLMQXTYDZXSQJTZPXLCGLQTZWJBHCTSYJSFXYEJJTLBGXSXJMYJQQPFZASYJNTYDJXKJCDJSZCBARTDCLYJQMWNQNCLLLKBYBZZSYHQQLTWLCCXTXLLZNTYLNEWYZYXCZXXGRKRMTCNDNJTSYYSSDQDGHSDBJGHRWRQLYBGLXHLGTGXBQJDZPYJSJYJCTMRNYMGRZJCZGJMZMGXMPRYXKJNYMSGMZJYMKMFXMLDTGFBHCJHKYLPFMDXLQJJSMTQGZSJLQDLDGJYCALCMZCSDJLLNXDJFFFFJCZFMZFFPFKHKGDPSXKTACJDHHZDDCRRCFQYJKQCCWJDXHWJLYLLZGCFCQDSMLZPBJJPLSBCJGGDCKKDEZSQCCKJGCGKDJTJDLZYCXKLQSCGJCLTFPCQCZGWPJDQYZJJBYJHSJDZWGFSJGZKQCCZLLPSPKJGQJHZZLJPLGJGJJTHJJYJZCZMLZLYQBGJWMLJKXZDZNJQSYZMLJLLJKYWXMKJLHSKJGBMCLYYMKXJQLBMLLKMDXXKWYXYSLMLPSJQQJQXYXFJTJDXMXXLLCXQBSYJBGWYMBGGBCYXPJYGPEPFGDJGBHBNSQJYZJKJKHXQFGQZKFHYGKHDKLLSDJQXPQYKYBNQSXQNSZSWHBSXWHXWBZZXDMNSJBSBKBBZKLYLXGWXDRWYQZMYWSJQLCJXXJXKJEQXSCYETLZHLYYYSDZPAQYZCMTLSHTZCFYZYXYLJSDCJQAGYSLCQLYYYSHMRQQKLDXZSCSSSYDYCJYSFSJBFRSSZQSBXXPXJYSDRCKGJLGDKZJZBDKTCSYQPYHSTCLDJDHMXMCGXYZHJDDTMHLTXZXYLYMOHYJCLTYFBQQXPFBDFHHTKSQHZYYWCNXXCRWHOWGYJLEGWDQCWGFJYCSNTMYTOLBYGWQWESJPWNMLRYDZSZTXYQPZGCWXHNGPYXSHMYQJXZTDPPBFYHZHTJYFDZWKGKZBLDNTSXHQEEGZZYLZMMZYJZGXZXKHKSTXNXXWYLYAPSTHXDWHZYMPXAGKYDXBHNHXKDPJNMYHYLPMGOCSLNZHKXXLPZZLBMLSFBHHGYGYYGGBHSCYAQTYWLXTZQCEZYDQDQMMHTKLLSZHLSJZWFYHQSWSCWLQAZYNYTLSXTHAZNKZZSZZLAXXZWWCTGQQTDDYZTCCHYQZFLXPSLZYGPZSZNGLNDQTBDLXGTCTAJDKYWNSYZLJHHZZCWNYYZYWMHYCHHYXHJKZWSXHZYXLYSKQYSPSLYZWMYPPKBYGLKZHTYXAXQSYSHXASMCHKDSCRSWJPWXSGZJLWWSCHSJHSQNHCSEGNDAQTBAALZZMSSTDQJCJKTSCJAXPLGGXHHGXXZCXPDMMHLDGTYBYSJMXHMRCPXXJZCKZXSHMLQXXTTHXWZFKHCCZDYTCJYXQHLXDHYPJQXYLSYYDZOZJNYXQEZYSQYAYXWYPDGXDDXSPPYZNDLTWRHXYDXZZJHTCXMCZLHPYYYYMHZLLHNXMYLLLMDCPPXHMXDKYCYRDLTXJCHHZZXZLCCLYLNZSHZJZZLNNRLWHYQSNJHXYNTTTKYJPYCHHYEGKCTTWLGQRLGGTGTYGYHPYHYLQYQGCWYQKPYYYTTTTLHYHLLTYTTSPLKYZXGZWGPYDSSZZDQXSKCQNMJJZZBXYQMJRTFFBTKHZKBXLJJKDXJTLBWFZPPTKQTZTGPDGNTPJYFALQMKGXBDCLZFHZCLLLLADPMXDJHLCCLGYHDZFGYDDGCYYFGYDXKSSEBDHYKDKDKHNAXXYBPBYYHXZQGAFFQYJXDMLJCSQZLLPCHBSXGJYNDYBYQSPZWJLZKSDDTACTBXZDYZYPJZQSJNKKTKNJDJGYYPGTLFYQKASDNTCYHBLWDZHBBYDWJRYGKZYHEYYFJMSDTYFZJJHGCXPLXHLDWXXJKYTCYKSSSMTWCTTQZLPBSZDZWZXGZAGYKTYWXLHLSPBCLLOQMMZSSLCMBJCSZZKYDCZJGQQDSMCYTZQQLWZQZXSSFPTTFQMDDZDSHDTDWFHTDYZJYQJQKYPBDJYYXTLJHDRQXXXHAYDHRJLKLYTWHLLRLLRCXYLBWSRSZZSYMKZZHHKYHXKSMDSYDYCJPBZBSQLFCXXXNXKXWYWSDZYQOGGQMMYHCDZTTFJYYBGSTTTYBYKJDHKYXBELHTYPJQNFXFDYKZHQKZBYJTZBXHFDXKDASWTAWAJLDYJSFHBLDNNTNQJTJNCHXFJSRFWHZFMDRYJYJWZPDJKZYJYMPCYZNYNXFBYTFYFWYGDBNZZZDNYTXZEMMQBSQEHXFZMBMFLZZSRXYMJGSXWZJSPRYDJSJGXHJJGLJJYNZZJXHGXKYMLPYYYCXYTWQZSWHWLYRJLPXSLSXMFSWWKLCTNXNYNPSJSZHDZEPTXMYYWXYYSYWLXJQZQXZDCLEEELMCPJPCLWBXSQHFWWTFFJTNQJHJQDXHWLBYZNFJLALKYYJLDXHHYCSTYYWNRJYXYWTRMDRQHWQCMFJDYZMHMYYXJWMYZQZXTLMRSPWWCHAQBXYGZYPXYYRRCLMPYMGKSJSZYSRMYJSNXTPLNBAPPYPYLXYYZKYNLDZYJZCZNNLMZHHARQMPGWQTZMXXMLLHGDZXYHXKYXYCJMFFYYHJFSBSSQLXXNDYCANNMTCJCYPRRNYTYQNYYMBMSXNDLYLYSLJRLXYSXQMLLYZLZJJJKYZZCSFBZXXMSTBJGNXYZHLXNMCWSCYZYFZLXBRNNNYLBNRTGZQYSATSWRYHYJZMZDHZGZDWYBSSCSKXSYHYTXXGCQGXZZSHYXJSCRHMKKBXCZJYJYMKQHZJFNBHMQHYSNJNZYBKNQMCLGQHWLZNZSWXKHLJHYYBQLBFCDSXDLDSPFZPSKJYZWZXZDDXJSMMEGJSCSSMGCLXXKYYYLNYPWWWGYDKZJGGGZGGSYCKNJWNJPCXBJJTQTJWDSSPJXZXNZXUMELPXFSXTLLXCLJXJJLJZXCTPSWXLYDHLYQRWHSYCSQYYBYAYWJJJQFWQCQQCJQGXALDBZZYJGKGXPLTZYFXJLTPADKYQHPMATLCPDCKBMTXYBHKLENXDLEEGQDYMSAWHZMLJTWYGXLYQZLJEEYYBQQFFNLYXRDSCTGJGXYYNKLLYQKCCTLHJLQMKKZGCYYGLLLJDZGYDHZWXPYSJBZKDZGYZZHYWYFQYTYZSZYEZZLYMHJJHTSMQWYZLKYYWZCSRKQYTLTDXWCTYJKLWSQZWBDCQYNCJSRSZJLKCDCDTLZZZACQQZZDDXYPLXZBQJYLZLLLQDDZQJYJYJZYXNYYYNYJXKXDAZWYRDLJYYYRJLXLLDYXJCYWYWNQCCLDDNYYYNYCKCZHXXCCLGZQJGKWPPCQQJYSBZZXYJSQPXJPZBSBDSFNSFPZXHDWZTDWPPTFLZZBZDMYYPQJRSDZSQZSQXBDGCPZSWDWCSQZGMDHZXMWWFYBPDGPHTMJTHZSMMBGZMBZJCFZWFZBBZMQCFMBDMCJXLGPNJBBXGYHYYJGPTZGZMQBQTCGYXJXLWZKYDPDYMGCFTPFXYZTZXDZXTGKMTYBBCLBJASKYTSSQYYMSZXFJEWLXLLSZBQJJJAKLYLXLYCCTSXMCWFKKKBSXLLLLJYXTYLTJYYTDPJHNHNNKBYQNFQYYZBYYESSESSGDYHFHWTCJBSDZZTFDMXHCNJZYMQWSRYJDZJQPDQBBSTJGGFBKJBXTGQHNGWJXJGDLLTHZHHYYYYYYSXWTYYYCCBDBPYPZYCCZYJPZYWCBDLFWZCWJDXXHYHLHWZZXJTCZLCDPXUJCZZZLYXJJTXPHFXWPYWXZPTDZZBDZCYHJHMLXBQXSBYLRDTGJRRCTTTHYTCZWMXFYTWWZCWJWXJYWCSKYBZSCCTZQNHXNWXXKHKFHTSWOCCJYBCMPZZYKBNNZPBZHHZDLSYDDYTYFJPXYNGFXBYQXCBHXCPSXTYZDMKYSNXSXLHKMZXLYHDHKWHXXSSKQYHHCJYXGLHZXCSNHEKDTGZXQYPKDHEXTYKCNYMYYYPKQYYYKXZLTHJQTBYQHXBMYHSQCKWWYLLHCYYLNNEQXQWMCFBDCCMLJGGXDQKTLXKGNQCDGZJWYJJLYHHQTTTNWCHMXCXWHWSZJYDJCCDBQCDGDNYXZTHCQRXCBHZTQCBXWGQWYYBXHMBYMYQTYEXMQKYAQYRGYZSLFYKKQHYSSQYSHJGJCNXKZYCXSBXYXHYYLSTYCXQTHYSMGSCPMMGCCCCCMTZTASMGQZJHKLOSQYLSWTMXSYQKDZLJQQYPLSYCZTCQQPBBQJZCLPKHQZYYXXDTDDTSJCXFFLLCHQXMJLWCJCXTSPYCXNDTJSHJWXDQQJSKXYAMYLSJHMLALYKXCYYDMNMDQMXMCZNNCYBZKKYFLMCHCMLHXRCJJHSYLNMTJZGZGYWJXSRXCWJGJQHQZDQJDCJJZKJKGDZQGJJYJYLXZXXCDQHHHEYTMHLFSBDJSYYSHFYSTCZQLPBDRFRZTZYKYWHSZYQKWDQZRKMSYNBCRXQBJYFAZPZZEDZCJYWBCJWHYJBQSZYWRYSZPTDKZPFPBNZTKLQYHBBZPNPPTYZZYBQNYDCPJMMCYCQMCYFZZDCMNLFPBPLNGQJTBTTNJZPZBBZNJKLJQYLNBZQHKSJZNGGQSZZKYXSHPZSNBCGZKDDZQANZHJKDRTLZLSWJLJZLYWTJNDJZJHXYAYNCBGTZCSSQMNJPJYTYSWXZFKWJQTKHTZPLBHSNJZSYZBWZZZZLSYLSBJHDWWQPSLMMFBJDWAQYZTCJTBNNWZXQXCDSLQGDSDPDZHJTQQPSWLYYJZLGYXYZLCTCBJTKTYCZJTQKBSJLGMGZDMCSGPYNJZYQYYKNXRPWSZXMTNCSZZYXYBYHYZAXYWQCJTLLCKJJTJHGDXDXYQYZZBYWDLWQCGLZGJGQRQZCZSSBCRPCSKYDZNXJSQGXSSJMYDNSTZTPBDLTKZWXQWQTZEXNQCZGWEZKSSBYBRTSSSLCCGBPSZQSZLCCGLLLZXHZQTHCZMQGYZQZNMCOCSZJMMZSQPJYGQLJYJPPLDXRGZYXCCSXHSHGTZNLZWZKJCXTCFCJXLBMQBCZZWPQDNHXLJCTHYZLGYLNLSZZPCXDSCQQHJQKSXZPBAJYEMSMJTZDXLCJYRYYNWJBNGZZTMJXLTBSLYRZPYLSSCNXPHLLHYLLQQZQLXYMRSYCXZLMMCZLTZSDWTJJLLNZGGQXPFSKYGYGHBFZPDKMWGHCXMSGDXJMCJZDYCABXJDLNBCDQYGSKYDQTXDJJYXMSZQAZDZFSLQXYJSJZYLBTXXWXQQZBJZUFBBLYLWDSLJHXJYZJWTDJCZFQZQZZDZSXZZQLZCDZFJHYSPYMPQZMLPPLFFXJJNZZYLSJEYQZFPFZKSYWJJJHRDJZZXTXXGLGHYDXCSKYSWMMZCWYBAZBJKSHFHJCXMHFQHYXXYZFTSJYZFXYXPZLCHMZMBXHZZSXYFYMNCWDABAZLXKTCSHHXKXJJZJSTHYGXSXYYHHHJWXKZXSSBZZWHHHCWTZZZPJXSNXQQJGZYZYWLLCWXZFXXYXYHXMKYYSWSQMNLNAYCYSPMJKHWCQHYLAJJMZXHMMCNZHBHXCLXTJPLTXYJHDYYLTTXFSZHYXXSJBJYAYRSMXYPLCKDUYHLXRLNLLSTYZYYQYGYHHSCCSMZCTZQXKYQFPYYRPFFLKQUNTSZLLZMWWTCQQYZWTLLMLMPWMBZSSTZRBPDDTLQJJBXZCSRZQQYGWCSXFWZLXCCRSZDZMCYGGDZQSGTJSWLJMYMMZYHFBJDGYXCCPSHXNZCSBSJYJGJMPPWAFFYFNXHYZXZYLREMZGZCYZSSZDLLJCSQFNXZKPTXZGXJJGFMYYYSNBTYLBNLHPFZDCYFBMGQRRSSSZXYSGTZRNYDZZCDGPJAFJFZKNZBLCZSZPSGCYCJSZLMLRSZBZZLDLSLLYSXSQZQLYXZLSKKBRXBRBZCYCXZZZEEYFGKLZLYYHGZSGZLFJHGTGWKRAAJYZKZQTSSHJJXDCYZUYJLZYRZDQQHGJZXSSZBYKJPBFRTJXLLFQWJHYLQTYMBLPZDXTZYGBDHZZRBGXHWNJTJXLKSCFSMWLSDQYSJTXKZSCFWJLBXFTZLLJZLLQBLSQMQQCGCZFPBPHZCZJLPYYGGDTGWDCFCZQYYYQYSSCLXZSKLZZZGFFCQNWGLHQYZJJCZLQZZYJPJZZBPDCCMHJGXDQDGDLZQMFGPSYTSDYFWWDJZJYSXYYCZCYHZWPBYKXRYLYBHKJKSFXTZJMMCKHLLTNYYMSYXYZPYJQYCSYCWMTJJKQYRHLLQXPSGTLYYCLJSCPXJYZFNMLRGJJTYZBXYZMSJYJHHFZQMSYXRSZCWTLRTQZSSTKXGQKGSPTGCZNJSJCQCXHMXGGZTQYDJKZDLBZSXJLHYQGGGTHQSZPYHJHHGYYGKGGCWJZZYLCZLXQSFTGZSLLLMLJSKCTBLLZZSZMMNYTPZSXQHJCJYQXYZXZQZCPSHKZZYSXCDFGMWQRLLQXRFZTLYSTCTMJCXJJXHJNXTNRZTZFQYHQGLLGCXSZSJDJLJCYDSJTLNYXHSZXCGJZYQPYLFHDJSBPCCZHJJJQZJQDYBSSLLCMYTTMQTBHJQNNYGKYRQYQMZGCJKPDCGMYZHQLLSLLCLMHOLZGDYYFZSLJCQZLYLZQJESHNYLLJXGJXLYSYYYXNBZLJSSZCQQCJYLLZLTJYLLZLLBNYLGQCHXYYXOXCXQKYJXXXYKLXSXXYQXCYKQXQCSGYXXYQXYGYTQOHXHXPYXXXULCYEYCHZZCBWQBBWJQZSCSZSSLZYLKDESJZWMYMCYTSDSXXSCJPQQSQYLYYZYCMDJDZYWCBTJSYDJKCYDDJLBDJJSODZYSYXQQYXDHHGQQYQHDYXWGMMMAJDYBBBPPBCMUUPLJZSMTXERXJMHQNUTPJDCBSSMSSSTKJTSSMMTRCPLZSZMLQDSDMJMQPNQDXCFYNBFSDQXYXHYAYKQYDDLQYYYSSZBYDSLNTFQTZQPZMCHDHCZCWFDXTMYQSPHQYYXSRGJCWTJTZZQMGWJJTJHTQJBBHWZPXXHYQFXXQYWYYHYSCDYDHHQMNMTMWCPBSZPPZZGLMZFOLLCFWHMMSJZTTDHZZYFFYTZZGZYSKYJXQYJZQBHMBZZLYGHGFMSHPZFZSNCLPBQSNJXZSLXXFPMTYJYGBXLLDLXPZJYZJYHHZCYWHJYLSJEXFSZZYWXKZJLUYDTMLYMQJPWXYHXSKTQJEZRPXXZHHMHWQPWQLYJJQJJZSZCPHJLCHHNXJLQWZJHBMZYXBDHHYPZLHLHLGFWLCHYYTLHJXCJMSCPXSTKPNHQXSRTYXXTESYJCTLSSLSTDLLLWWYHDHRJZSFGXTSYCZYNYHTDHWJSLHTZDQDJZXXQHGYLTZPHCSQFCLNJTCLZPFSTPDYNYLGMJLLYCQHYSSHCHYLHQYQTMZYPBYWRFQYKQSYSLZDQJMPXYYSSRHZJNYWTQDFZBWWTWWRXCWHGYHXMKMYYYQMSMZHNGCEPMLQQMTCWCTMMPXJPJJHFXYYZSXZHTYBMSTSYJTTQQQYYLHYNPYQZLCYZHZWSMYLKFJXLWGXYPJYTYSYXYMZCKTTWLKSMZSYLMPWLZWXWQZSSAQSYXYRHSSNTSRAPXCPWCMGDXHXZDZYFJHGZTTSBJHGYZSZYSMYCLLLXBTYXHBBZJKSSDMALXHYCFYGMQYPJYCQXJLLLJGSLZGQLYCJCCZOTYXMTMTTLLWTGPXYMZMKLPSZZZXHKQYSXCTYJZYHXSHYXZKXLZWPSQPYHJWPJPWXQQYLXSDHMRSLZZYZWTTCYXYSZZSHBSCCSTPLWSSCJCHNLCGCHSSPHYLHFHHXJSXYLLNYLSZDHZXYLSXLWZYKCLDYAXZCMDDYSPJTQJZLNWQPSSSWCTSTSZLBLNXSMNYYMJQBQHRZWTYYDCHQLXKPZWBGQYBKFCMZWPZLLYYLSZYDWHXPSBCMLJBSCGBHXLQHYRLJXYSWXWXZSLDFHLSLYNJLZYFLYJYCDRJLFSYZFSLLCQYQFGJYHYXZLYLMSTDJCYHBZLLNWLXXYGYYHSMGDHXXHHLZZJZXCZZZCYQZFNGWPYLCPKPYYPMCLQKDGXZGGWQBDXZZKZFBXXLZXJTPJPTTBYTSZZDWSLCHZHSLTYXHQLHYXXXYYZYSWTXZKHLXZXZPYHGCHKCFSYHUTJRLXFJXPTZTWHPLYXFCRHXSHXKYXXYHZQDXQWULHYHMJTBFLKHTXCWHJFWJCFPQRYQXCYYYQYGRPYWSGSUNGWCHKZDXYFLXXHJJBYZWTSXXNCYJJYMSWZJQRMHXZWFQSYLZJZGBHYNSLBGTTCSYBYXXWXYHXYYXNSQYXMQYWRGYQLXBBZLJSYLPSYTJZYHYZAWLRORJMKSCZJXXXYXCHDYXRYXXJDTSQFXLYLTSFFYXLMTYJMJUYYYXLTZCSXQZQHZXLYYXZHDNBRXXXJCTYHLBRLMBRLLAXKYLLLJLYXXLYCRYLCJTGJCMTLZLLCYZZPZPCYAWHJJFYBDYYZSMPCKZDQYQPBPCJPDCYZMDPBCYYDYCNNPLMTMLRMFMMGWYZBSJGYGSMZQQQZTXMKQWGXLLPJGZBQCDJJJFPKJKCXBLJMSWMDTQJXLDLPPBXCWRCQFBFQJCZAHZGMYKPHYYHZYKNDKZMBPJYXPXYHLFPNYYGXJDBKXNXHJMZJXSTRSTLDXSKZYSYBZXJLXYSLBZYSLHXJPFXPQNBYLLJQKYGZMCYZZYMCCSLCLHZFWFWYXZMWSXTYNXJHPYYMCYSPMHYSMYDYSHQYZCHMJJMZCAAGCFJBBHPLYZYLXXSDJGXDHKXXTXXNBHRMLYJSLTXMRHNLXQJXYZLLYSWQGDLBJHDCGJYQYCMHWFMJYBMBYJYJWYMDPWHXQLDYGPDFXXBCGJSPCKRSSYZJMSLBZZJFLJJJLGXZGYXYXLSZQYXBEXYXHGCXBPLDYHWETTWWCJMBTXCHXYQXLLXFLYXLLJLSSFWDPZSMYJCLMWYTCZPCHQEKCQBWLCQYDPLQPPQZQFJQDJHYMMCXTXDRMJWRHXCJZYLQXDYYNHYYHRSLSRSYWWZJYMTLTLLGTQCJZYABTCKZCJYCCQLJZQXALMZYHYWLWDXZXQDLLQSHGPJFJLJHJABCQZDJGTKHSSTCYJLPSWZLXZXRWGLDLZRLZXTGSLLLLZLYXXWGDZYGBDPHZPBRLWSXQBPFDWOFMWHLYPCBJCCLDMBZPBZZLCYQXLDOMZBLZWPDWYYGDSTTHCSQSCCRSSSYSLFYBFNTYJSZDFNDPDHDZZMBBLSLCMYFFGTJJQWFTMTPJWFNLBZCMMJTGBDZLQLPYFHYYMJYLSDCHDZJWJCCTLJCLDTLJJCPDDSQDSSZYBNDBJLGGJZXSXNLYCYBJXQYCBYLZCFZPPGKCXZDZFZTJJFJSJXZBNZYJQTTYJYHTYCZHYMDJXTTMPXSPLZCDWSLSHXYPZGTFMLCJTYCBPMGDKWYCYZCDSZZYHFLYCTYGWHKJYYLSJCXGYWJCBLLCSNDDBTZBSCLYZCZZSSQDLLMQYYHFSLQLLXFTYHABXGWNYWYYPLLSDLDLLBJCYXJZMLHLJDXYYQYTDLLLBUGBFDFBBQJZZMDPJHGCLGMJJPGAEHHBWCQXAXHHHZCHXYPHJAXHLPHJPGPZJQCQZGJJZZUZDMQYYBZZPHYHYBWHAZYJHYKFGDPFQSDLZMLJXKXGALXZDAGLMDGXMWZQYXXDXXPFDMMSSYMPFMDMMKXKSYZYSHDZKXSYSMMZZZMSYDNZZCZXFPLSTMZDNMXCKJMZTYYMZMZZMSXHHDCZJEMXXKLJSTLWLSQLYJZLLZJSSDPPMHNLZJCZYHMXXHGZCJMDHXTKGRMXFWMCGMWKDTKSXQMMMFZZYDKMSCLCMPCGMHSPXQPZDSSLCXKYXTWLWJYAHZJGZQMCSNXYYMMPMLKJXMHLMLQMXCTKZMJQYSZJSYSZHSYJZJCDAJZYBSDQJZGWZQQXFKDMSDJLFWEHKZQKJPEYPZYSZCDWYJFFMZZYLTTDZZEFMZLBNPPLPLPEPSZALLTYLKCKQZKGENQLWAGYXYDPXLHSXQQWQCQXQCLHYXXMLYCCWLYMQYSKGCHLCJNSZKPYZKCQZQLJPDMDZHLASXLBYDWQLWDNBQCRYDDZTJYBKBWSZDXDTNPJDTCTQDFXQQMGNXECLTTBKPWSLCTYQLPWYZZKLPYGZCQQPLLKCCYLPQMZCZQCLJSLQZDJXLDDHPZQDLJJXZQDXYZQKZLJCYQDYJPPYPQYKJYRMPCBYMCXKLLZLLFQPYLLLMBSGLCYSSLRSYSQTMXYXZQZFDZUYSYZTFFMZZSMZQHZSSCCMLYXWTPZGXZJGZGSJSGKDDHTQGGZLLBJDZLCBCHYXYZHZFYWXYZYMSDBZZYJGTSMTFXQYXQSTDGSLNXDLRYZZLRYYLXQHTXSRTZNGZXBNQQZFMYKMZJBZYMKBPNLYZPBLMCNQYZZZSJZHJCTZKHYZZJRDYZHNPXGLFZTLKGJTCTSSYLLGZRZBBQZZKLPKLCZYSSUYXBJFPNJZZXCDWXZYJXZZDJJKGGRSRJKMSMZJLSJYWQSKYHQJSXPJZZZLSNSHRNYPZTWCHKLPSRZLZXYJQXQKYSJYCZTLQZYBBYBWZPQDWWYZCYTJCJXCKCWDKKZXSGKDZXWWYYJQYYTCYTDLLXWKCZKKLCCLZCQQDZLQLCSFQCHQHSFSMQZZLNBJJZBSJHTSZDYSJQJPDLZCDCWJKJZZLPYCGMZWDJJBSJQZSYZYHHXJPBJYDSSXDZNCGLQMBTSFSBPDZDLZNFGFJGFSMPXJQLMBLGQCYYXBQKDJJQYRFKZTJDHCZKLBSDZCFJTPLLJGXHYXZCSSZZXSTJYGKGCKGYOQXJPLZPBPGTGYJZGHZQZZLBJLSQFZGKQQJZGYCZBZQTLDXRJXBSXXPZXHYZYCLWDXJJHXMFDZPFZHQHQMQGKSLYHTYCGFRZGNQXCLPDLBZCSCZQLLJBLHBZCYPZZPPDYMZZSGYHCKCPZJGSLJLNSCDSLDLXBMSTLDDFJMKDJDHZLZXLSZQPQPGJLLYBDSZGQLBZLSLKYYHZTTNTJYQTZZPSZQZTLLJTYYLLQLLQYZQLBDZLSLYYZYMDFSZSNHLXZNCZQZPBWSKRFBSYZMTHBLGJPMCZZLSTLXSHTCSYZLZBLFEQHLXFLCJLYLJQCBZLZJHHSSTBRMHXZHJZCLXFNBGXGTQJCZTMSFZKJMSSNXLJKBHSJXNTNLZDNTLMSJXGZJYJCZXYJYJWRWWQNZTNFJSZPZSHZJFYRDJSFSZJZBJFZQZZHZLXFYSBZQLZSGYFTZDCSZXZJBQMSZKJRHYJZCKMJKHCHGTXKXQGLXPXFXTRTYLXJXHDTSJXHJZJXZWZLCQSBTXWXGXTXXHXFTSDKFJHZYJFJXRZSDLLLTQSQQZQWZXSYQTWGWBZCGZLLYZBCLMQQTZHZXZXLJFRMYZFLXYSQXXJKXRMQDZDMMYYBSQBHGZMWFWXGMXLZPYYTGZYCCDXYZXYWGSYJYZNBHPZJSQSYXSXRTFYZGRHZTXSZZTHCBFCLSYXZLZQMZLMPLMXZJXSFLBYZMYQHXJSXRXSQZZZSSLYFRCZJRCRXHHZXQYDYHXSJJHZCXZBTYNSYSXJBQLPXZQPYMLXZKYXLXCJLCYSXXZZLXDLLLJJYHZXGYJWKJRWYHCPSGNRZLFZWFZZNSXGXFLZSXZZZBFCSYJDBRJKRDHHGXJLJJTGXJXXSTJTJXLYXQFCSGSWMSBCTLQZZWLZZKXJMLTMJYHSDDBXGZHDLBMYJFRZFSGCLYJBPMLYSMSXLSZJQQHJZFXGFQFQBPXZGYYQXGZTCQWYLTLGWSGWHRLFSFGZJMGMGBGTJFSYZZGZYZAFLSSPMLPFLCWBJZCLJJMZLPJJLYMQDMYYYFBGYGYZMLYZDXQYXRQQQHSYYYQXYLJTYXFSFSLLGNQCYHYCWFHCCCFXPYLYPLLZYXXXXXKQHHXSHJZCFZSCZJXCPZWHHHHHAPYLQALPQAFYHXDYLUKMZQGGGDDESRNNZLTZGCHYPPYSQJJHCLLJTOLNJPZLJLHYMHEYDYDSQYCDDHGZUNDZCLZYZLLZNTNYZGSLHSLPJJBDGWXPCDUTJCKLKCLWKLLCASSTKZZDNQNTTLYYZSSYSSZZRYLJQKCQDHHCRXRZYDGRGCWCGZQFFFPPJFZYNAKRGYWYQPQXXFKJTSZZXSWZDDFBBXTBGTZKZNPZZPZXZPJSZBMQHKCYXYLDKLJNYPKYGHGDZJXXEAHPNZKZTZCMXCXMMJXNKSZQNMNLWBWWXJKYHCPSTMCSQTZJYXTPCTPDTNNPGLLLZSJLSPBLPLQHDTNJNLYYRSZFFJFQWDPHZDWMRZCCLODAXNSSNYZRESTYJWJYJDBCFXNMWTTBYLWSTSZGYBLJPXGLBOCLHPCBJLTMXZLJYLZXCLTPNCLCKXTPZJSWCYXSFYSZDKNTLBYJCYJLLSTGQCBXRYZXBXKLYLHZLQZLNZCXWJZLJZJNCJHXMNZZGJZZXTZJXYCYYCXXJYYXJJXSSSJSTSSTTPPGQTCSXWZDCSYFPTFBFHFBBLZJCLZZDBXGCXLQPXKFZFLSYLTUWBMQJHSZBMDDBCYSCCLDXYCDDQLYJJWMQLLCSGLJJSYFPYYCCYLTJANTJJPWYCMMGQYYSXDXQMZHSZXPFTWWZQSWQRFKJLZJQQYFBRXJHHFWJJZYQAZMYFRHCYYBYQWLPEXCCZSTYRLTTDMQLYKMBBGMYYJPRKZNPBSXYXBHYZDJDNGHPMFSGMWFZMFQMMBCMZZCJJLCNUXYQLMLRYGQZCYXZLWJGCJCGGMCJNFYZZJHYCPRRCMTZQZXHFQGTJXCCJEAQCRJYHPLQLSZDJRBCQHQDYRHYLYXJSYMHZYDWLDFRYHBPYDTSSCNWBXGLPZMLZZTQSSCPJMXXYCSJYTYCGHYCJWYRXXLFEMWJNMKLLSWTXHYYYNCMMCWJDQDJZGLLJWJRKHPZGGFLCCSCZMCBLTBHBQJXQDSPDJZZGKGLFQYWBZYZJLTSTDHQHCTCBCHFLQMPWDSHYYTQWCNZZJTLBYMBPDYYYXSQKXWYYFLXXNCWCXYPMAELYKKJMZZZBRXYYQJFLJPFHHHYTZZXSGQQMHSPGDZQWBWPJHZJDYSCQWZKTXXSQLZYYMYSDZGRXCKKUJLWPYSYSCSYZLRMLQSYLJXBCXTLWDQZPCYCYKPPPNSXFYZJJRCEMHSZMSXLXGLRWGCSTLRSXBZGBZGZTCPLUJLSLYLYMTXMTZPALZXPXJTJWTCYYZLBLXBZLQMYLXPGHDSLSSDMXMBDZZSXWHAMLCZCPJMCNHJYSNSYGCHSKQMZZQDLLKABLWJXSFMOCDXJRRLYQZKJMYBYQLYHETFJZFRFKSRYXFJTWDSXXSYSQJYSLYXWJHSNLXYYXHBHAWHHJZXWMYLJCSSLKYDZTXBZSYFDXGXZJKHSXXYBSSXDPYNZWRPTQZCZENYGCXQFJYKJBZMLJCMQQXUOXSLYXXLYLLJDZBTYMHPFSTTQQWLHOKYBLZZALZXQLHZWRRQHLSTMYPYXJJXMQSJFNBXYXYJXXYQYLTHYLQYFMLKLJTMLLHSZWKZHLJMLHLJKLJSTLQXYLMBHHLNLZXQJHXCFXXLHYHJJGBYZZKBXSCQDJQDSUJZYYHZHHMGSXCSYMXFEBCQWWRBPYYJQTYZCYQYQQZYHMWFFHGZFRJFCDPXNTQYZPDYKHJLFRZXPPXZDBBGZQSTLGDGYLCQMLCHHMFYWLZYXKJLYPQHSYWMQQGQZMLZJNSQXJQSYJYCBEHSXFSZPXZWFLLBCYYJDYTDTHWZSFJMQQYJLMQXXLLDTTKHHYBFPWTYYSQQWNQWLGWDEBZWCMYGCULKJXTMXMYJSXHYBRWFYMWFRXYQMXYSZTZZTFYKMLDHQDXWYYNLCRYJBLPSXCXYWLSPRRJWXHQYPHTYDNXHHMMYWYTZCSQMTSSCCDALWZTCPQPYJLLQZYJSWXMZZMMYLMXCLMXCZMXMZSQTZPPQQBLPGXQZHFLJJHYTJSRXWZXSCCDLXTYJDCQJXSLQYCLZXLZZXMXQRJMHRHZJBHMFLJLMLCLQNLDXZLLLPYPSYJYSXCQQDCMQJZZXHNPNXZMEKMXHYKYQLXSXTXJYYHWDCWDZHQYYBGYBCYSCFGPSJNZDYZZJZXRZRQJJYMCANYRJTLDPPYZBSTJKXXZYPFDWFGZZRPYMTNGXZQBYXNBUFNQKRJQZMJEGRZGYCLKXZDSKKNSXKCLJSPJYYZLQQJYBZSSQLLLKJXTBKTYLCCDDBLSPPFYLGYDTZJYQGGKQTTFZXBDKTYYHYBBFYTYYBCLPDYTGDHRYRNJSPTCSNYJQHKLLLZSLYDXXWBCJQSPXBPJZJCJDZFFXXBRMLAZHCSNDLBJDSZBLPRZTSWSBXBCLLXXLZDJZSJPYLYXXYFTFFFBHJJXGBYXJPMMMPSSJZJMTLYZJXSWXTYLEDQPJMYGQZJGDJLQJWJQLLSJGJGYGMSCLJJXDTYGJQJQJCJZCJGDZZSXQGSJGGCXHQXSNQLZZBXHSGZXCXYLJXYXYYDFQQJHJFXDHCTXJYRXYSQTJXYEFYYSSYYJXNCYZXFXMSYSZXYYSCHSHXZZZGZZZGFJDLTYLNPZGYJYZYYQZPBXQBDZTZCZYXXYHHSQXSHDHGQHJHGYWSZTMZMLHYXGEBTYLZKQWYTJZRCLEKYSTDBCYKQQSAYXCJXWWGSBHJYZYDHCSJKQCXSWXFLTYNYZPZCCZJQTZWJQDZZZQZLJJXLSBHPYXXPSXSHHEZTXFPTLQYZZXHYTXNCFZYYHXGNXMYWXTZSJPTHHGYMXMXQZXTSBCZYJYXXTYYZYPCQLMMSZMJZZLLZXGXZAAJZYXJMZXWDXZSXZDZXLEYJJZQBHZWZZZQTZPSXZTDSXJJJZNYAZPHXYYSRNQDTHZHYYKYJHDZXZLSWCLYBZYECWCYCRYLCXNHZYDZYDYJDFRJJHTRSQTXYXJRJHOJYNXELXSFSFJZGHPZSXZSZDZCQZBYYKLSGSJHCZSHDGQGXYZGXCHXZJWYQWGYHKSSEQZZNDZFKWYSSTCLZSTSYMCDHJXXYWEYXCZAYDMPXMDSXYBSQMJMZJMTZQLPJYQZCGQHXJHHLXXHLHDLDJQCLDWBSXFZZYYSCHTYTYYBHECXHYKGJPXHHYZJFXHWHBDZFYZBCAPNPGNYDMSXHMMMMAMYNBYJTMPXYYMCTHJBZYFCGTYHWPHFTWZZEZSBZEGPFMTSKFTYCMHFLLHGPZJXZJGZJYXZSBBQSCZZLZCCSTPGXMJSFTCCZJZDJXCYBZLFCJSYZFGSZLYBCWZZBYZDZYPSWYJZXZBDSYUXLZZBZFYGCZXBZHZFTPBGZGEJBSTGKDMFHYZZJHZLLZZGJQZLSFDJSSCBZGPDLFZFZSZYZYZSYGCXSNXXCHCZXTZZLJFZGQSQYXZJQDCCZTQCDXZJYQJQCHXZTDLGSCXZSYQJQTZWLQDQZTQCHQQJZYEZZZPBWKDJFCJPZTYPQYQTTYNLMBDKTJZPQZQZZFPZSBNJLGYJDXJDZZKZGQKXDLPZJTCJDQBXDJQJSTCKNXBXZMSLYJCQMTJQWWCJQNJNLLLHJCWQTBZQYDZCZPZZDZYDDCYZZZCCJTTJFZDPRRTZTJDCQTQZDTJNPLZBCLLCTZSXKJZQZPZLBZRBTJDCXFCZDBCCJJLTQQPLDCGZDBBZJCQDCJWYNLLZYZCCDWLLXWZLXRXNTQQCZXKQLSGDFQTDDGLRLAJJTKUYMKQLLTZYTDYYCZGJWYXDXFRSKSTQTENQMRKQZHHQKDLDAZFKYPBGGPZREBZZYKZZSPEGJXGYKQZZZSLYSYYYZWFQZYLZZLZHWCHKYPQGNPGBLPLRRJYXCCSYYHSFZFYBZYYTGZXYLXCZWXXZJZBLFFLGSKHYJZEYJHLPLLLLCZGXDRZELRHGKLZZYHZLYQSZZJZQLJZFLNBHGWLCZCFJYSPYXZLZLXGCCPZBLLCYBBBBUBBCBPCRNNZCZYRBFSRLDCGQYYQXYGMQZWTZYTYJXYFWTEHZZJYWLCCNTZYJJZDEDPZDZTSYQJHDYMBJNYJZLXTSSTPHNDJXXBYXQTZQDDTJTDYYTGWSCSZQFLSHLGLBCZPHDLYZJYCKWTYTYLBNYTSDSYCCTYSZYYEBHEXHQDTWNYGYCLXTSZYSTQMYGZAZCCSZZDSLZCLZRQXYYELJSBYMXSXZTEMBBLLYYLLYTDQYSHYMRQWKFKBFXNXSBYCHXBWJYHTQBPBSBWDZYLKGZSKYHXQZJXHXJXGNLJKZLYYCDXLFYFGHLJGJYBXQLYBXQPQGZTZPLNCYPXDJYQYDYMRBESJYYHKXXSTMXRCZZYWXYQYBMCLLYZHQYZWQXDBXBZWZMSLPDMYSKFMZKLZCYQYCZLQXFZZYDQZPZYGYJYZMZXDZFYFYTTQTZHGSPCZMLCCYTZXJCYTJMKSLPZHYSNZLLYTPZCTZZCKTXDHXXTQCYFKSMQCCYYAZHTJPCYLZLYJBJXTPNYLJYYNRXSYLMMNXJSMYBCSYSYLZYLXJJQYLDZLPQBFZZBLFNDXQKCZFYWHGQMRDSXYCYTXNQQJZYYPFZXDYZFPRXEJDGYQBXRCNFYYQPGHYJDYZXGRHTKYLNWDZNTSMPKLBTHBPYSZBZTJZSZZJTYYXZPHSSZZBZCZPTQFZMYFLYPYBBJQXZMXXDJMTSYSKKBJZXHJCKLPSMKYJZCXTMLJYXRZZQSLXXQPYZXMKYXXXJCLJPRMYYGADYSKQLSNDHYZKQXZYZTCGHZTLMLWZYBWSYCTBHJHJFCWZTXWYTKZLXQSHLYJZJXTMPLPYCGLTBZZTLZJCYJGDTCLKLPLLQPJMZPAPXYZLKKTKDZCZZBNZDYDYQZJYJGMCTXLTGXSZLMLHBGLKFWNWZHDXUHLFMKYSLGXDTWWFRJEJZTZHYDXYKSHWFZCQSHKTMQQHTZHYMJDJSKHXZJZBZZXYMPAGQMSTPXLSKLZYNWRTSQLSZBPSPSGZWYHTLKSSSWHZZLYYTNXJGMJSZSUFWNLSOZTXGXLSAMMLBWLDSZYLAKQCQCTMYCFJBSLXCLZZCLXXKSBZQCLHJPSQPLSXXCKSLNHPSFQQYTXYJZLQLDXZQJZDYYDJNZPTUZDSKJFSLJHYLZSQZLBTXYDGTQFDBYAZXDZHZJNHHQBYKNXJJQCZMLLJZKSPLDYCLBBLXKLELXJLBQYCXJXGCNLCQPLZLZYJTZLJGYZDZPLTQCSXFDMNYCXGBTJDCZNBGBQYQJWGKFHTNPYQZQGBKPBBYZMTJDYTBLSQMPSXTBNPDXKLEMYYCJYNZCTLDYKZZXDDXHQSHDGMZSJYCCTAYRZLPYLTLKXSLZCGGEXCLFXLKJRTLQJAQZNCMBYDKKCXGLCZJZXJHPTDJJMZQYKQSECQZDSHHADMLZFMMZBGNTJNNLGBYJBRBTMLBYJDZXLCJLPLDLPCQDHLXZLYCBLCXZZJADJLNZMMSSSMYBHBSQKBHRSXXJMXSDZNZPXLGBRHWGGFCXGMSKLLTSJYYCQLTSKYWYYHYWXBXQYWPYWYKQLSQPTNTKHQCWDQKTWPXXHCPTHTWUMSSYHBWCRWXHJMKMZNGWTMLKFGHKJYLSYYCXWHYECLQHKQHTTQKHFZLDXQWYZYYDESBPKYRZPJFYYZJCEQDZZDLATZBBFJLLCXDLMJSSXEGYGSJQXCWBXSSZPDYZCXDNYXPPZYDLYJCZPLTXLSXYZYRXCYYYDYLWWNZSAHJSYQYHGYWWAXTJZDAXYSRLTDPSSYYFNEJDXYZHLXLLLZQZSJNYQYQQXYJGHZGZCYJCHZLYCDSHWSHJZYJXCLLNXZJJYYXNFXMWFPYLCYLLABWDDHWDXJMCXZTZPMLQZHSFHZYNZTLLDYWLSLXHYMMYLMBWWKYXYADTXYLLDJPYBPWUXJMWMLLSAFDLLYFLBHHHBQQLTZJCQJLDJTFFKMMMBYTHYGDCQRDDWRQJXNBYSNWZDBYYTBJHPYBYTTJXAAHGQDQTMYSTQXKBTZPKJLZRBEQQSSMJJBDJOTGTBXPGBKTLHQXJJJCTHXQDWJLWRFWQGWSHCKRYSWGFTGYGBXSDWDWRFHWYTJJXXXJYZYSLPYYYPAYXHYDQKXSHXYXGSKQHYWFDDDPPLCJLQQEEWXKSYYKDYPLTJTHKJLTCYYHHJTTPLTZZCDLTHQKZXQYSTEEYWYYZYXXYYSTTJKLLPZMCYHQGXYHSRMBXPLLNQYDQHXSXXWGDQBSHYLLPJJJTHYJKYPPTHYYKTYEZYENMDSHLCRPQFDGFXZPSFTLJXXJBSWYYSKSFLXLPPLBBBLBSFXFYZBSJSSYLPBBFFFFSSCJDSTZSXZRYYSYFFSYZYZBJTBCTSBSDHRTJJBYTCXYJEYLXCBNEBJDSYXYKGSJZBXBYTFZWGENYHHTHZHHXFWGCSTBGXKLSXYWMTMBYXJSTZSCDYQRCYTWXZFHMYMCXLZNSDJTTTXRYCFYJSBSDYERXJLJXBBDEYNJGHXGCKGSCYMBLXJMSZNSKGXFBNBPTHFJAAFXYXFPXMYPQDTZCXZZPXRSYWZDLYBBKTYQPQJPZYPZJZNJPZJLZZFYSBTTSLMPTZRTDXQSJEHBZYLZDHLJSQMLHTXTJECXSLZZSPKTLZKQQYFSYGYWPCPQFHQHYTQXZKRSGTTSQCZLPTXCDYYZXSQZSLXLZMYCPCQBZYXHBSXLZDLTCDXTYLZJYYZPZYZLTXJSJXHLPMYTXCQRBLZSSFJZZTNJYTXMYJHLHPPLCYXQJQQKZZSCPZKSWALQSBLCCZJSXGWWWYGYKTJBBZTDKHXHKGTGPBKQYSLPXPJCKBMLLXDZSTBKLGGQKQLSBKKTFXRMDKBFTPZFRTBBRFERQGXYJPZSSTLBZTPSZQZSJDHLJQLZBPMSMMSXLQQNHKNBLRDDNXXDHDDJCYYGYLXGZLXSYGMQQGKHBPMXYXLYTQWLWGCPBMQXCYZYDRJBHTDJYHQSHTMJSBYPLWHLZFFNYPMHXXHPLTBQPFBJWQDBYGPNZTPFZJGSDDTQSHZEAWZZYLLTYYBWJKXXGHLFKXDJTMSZSQYNZGGSWQSPHTLSSKMCLZXYSZQZXNCJDQGZDLFNYKLJCJLLZLMZZNHYDSSHTHZZLZZBBHQZWWYCRZHLYQQJBEYFXXXWHSRXWQHWPSLMSSKZTTYGYQQWRSLALHMJTQJSMXQBJJZJXZYZKXBYQXBJXSHZTSFJLXMXZXFGHKZSZGGYLCLSARJYHSLLLMZXELGLXYDJYTLFBHBPNLYZFBBHPTGJKWETZHKJJXZXXGLLJLSTGSHJJYQLQZFKCGNNDJSSZFDBCTWWSEQFHQJBSAQTGYPQLBXBMMYWXGSLZHGLZGQYFLZBYFZJFRYSFMBYZHQGFWZSYFYJJPHZBYYZFFWODGRLMFTWLBZGYCQXCDJYGZYYYYTYTYDWEGAZYHXJLZYYHLRMGRXXZCLHNELJJTJTPWJYBJJBXJJTJTEEKHWSLJPLPSFYZPQQBDLQJJTYYQLYZKDKSQJYYQZLDQTGJQYZJSUCMRYQTHTEJMFCTYHYPKMHYZWJDQFHYYXWSHCTXRLJHQXHCCYYYJLTKTTYTMXGTCJTZAYYOCZLYLBSZYWJYTSJYHBYSHFJLYGJXXTMZYYLTXXYPZLXYJZYZYYPNHMYMDYYLBLHLSYYQQLLNJJYMSOYQBZGDLYXYLCQYXTSZEGXHZGLHWBLJHEYXTWQMAKBPQCGYSHHEGQCMWYYWLJYJHYYZLLJJYLHZYHMGSLJLJXCJJYCLYCJPCPZJZJMMYLCQLNQLJQJSXYJMLSZLJQLYCMMHCFMMFPQQMFYLQMCFFQMMMMHMZNFHHJGTTHHKHSLNCHHYQDXTMMQDCYZYXYQMYQYLTDCYYYZAZZCYMZYDLZFFFMMYCQZWZZMABTBYZTDMNZZGGDFTYPCGQYTTSSFFWFDTZQSSYSTWXJHXYTSXXYLBYQHWWKXHZXWZNNZZJZJJQJCCCHYYXBZXZCYZTLLCQXYNJYCYYCYNZZQYYYEWYCZDCJYCCHYJLBTZYYCQWMPWPYMLGKDLDLGKQQBGYCHJXY";
        //此处收了375个多音字
        var oMultiDiff={"19969":"DZ","19975":"WM","19988":"QJ","20048":"YL","20056":"SC","20060":"NM","20094":"QG","20127":"QJ","20167":"QC","20193":"YG","20250":"KH","20256":"ZC","20282":"SC","20285":"QJG","20291":"TD","20314":"YD","20340":"NE","20375":"TD","20389":"YJ","20391":"CZ","20415":"PB","20446":"YS","20447":"SQ","20504":"TC","20608":"KG","20854":"QJ","20857":"ZC","20911":"PF","20985":"AW","21032":"PB","21048":"XQ","21049":"SC","21089":"YS","21119":"JC","21242":"SB","21273":"SC","21305":"YP","21306":"QO","21330":"ZC","21333":"SDC","21345":"QK","21378":"CA","21397":"SC","21414":"XS","21442":"SC","21477":"JG","21480":"TD","21484":"ZS","21494":"YX","21505":"YX","21512":"HG","21523":"XH","21537":"PB","21542":"PF","21549":"KH","21571":"E","21574":"DA","21588":"TD","21589":"O","21618":"ZC","21621":"KHA","21632":"ZJ","21654":"KG","21679":"LKG","21683":"KH","21710":"A","21719":"YH","21734":"WOE","21769":"A","21780":"WN","21804":"XH","21834":"A","21899":"ZD","21903":"RN","21908":"WO","21939":"ZC","21956":"SA","21964":"YA","21970":"TD","22003":"A","22031":"JG","22040":"XS","22060":"ZC","22066":"ZC","22079":"MH","22129":"XJ","22179":"XA","22237":"NJ","22244":"TD","22280":"JQ","22300":"YH","22313":"XW","22331":"YQ","22343":"YJ","22351":"PH","22395":"DC","22412":"TD","22484":"PB","22500":"PB","22534":"ZD","22549":"DH","22561":"PB","22612":"TD","22771":"KQ","22831":"HB","22841":"JG","22855":"QJ","22865":"XQ","23013":"ML","23081":"WM","23487":"SX","23558":"QJ","23561":"YW","23586":"YW","23614":"YW","23615":"SN","23631":"PB","23646":"ZS","23663":"ZT","23673":"YG","23762":"TD","23769":"ZS","23780":"QJ","23884":"QK","24055":"XH","24113":"DC","24162":"ZC","24191":"GA","24273":"QJ","24324":"NL","24377":"TD","24378":"QJ","24439":"PF","24554":"ZS","24683":"TD","24694":"WE","24733":"LK","24925":"TN","25094":"ZG","25100":"XQ","25103":"XH","25153":"PB","25170":"PB","25179":"KG","25203":"PB","25240":"ZS","25282":"FB","25303":"NA","25324":"KG","25341":"ZY","25373":"WZ","25375":"XJ","25384":"A","25457":"A","25528":"SD","25530":"SC","25552":"TD","25774":"ZC","25874":"ZC","26044":"YW","26080":"WM","26292":"PB","26333":"PB","26355":"ZY","26366":"CZ","26397":"ZC","26399":"QJ","26415":"ZS","26451":"SB","26526":"ZC","26552":"JG","26561":"TD","26588":"JG","26597":"CZ","26629":"ZS","26638":"YL","26646":"XQ","26653":"KG","26657":"XJ","26727":"HG","26894":"ZC","26937":"ZS","26946":"ZC","26999":"KJ","27099":"KJ","27449":"YQ","27481":"XS","27542":"ZS","27663":"ZS","27748":"TS","27784":"SC","27788":"ZD","27795":"TD","27812":"O","27850":"PB","27852":"MB","27895":"SL","27898":"PL","27973":"QJ","27981":"KH","27986":"HX","27994":"XJ","28044":"YC","28065":"WG","28177":"SM","28267":"QJ","28291":"KH","28337":"ZQ","28463":"TL","28548":"DC","28601":"TD","28689":"PB","28805":"JG","28820":"QG","28846":"PB","28952":"TD","28975":"ZC","29100":"A","29325":"QJ","29575":"SL","29602":"FB","30010":"TD","30044":"CX","30058":"PF","30091":"YSP","30111":"YN","30229":"XJ","30427":"SC","30465":"SX","30631":"YQ","30655":"QJ","30684":"QJG","30707":"SD","30729":"XH","30796":"LG","30917":"PB","31074":"NM","31085":"JZ","31109":"SC","31181":"ZC","31192":"MLB","31293":"JQ","31400":"YX","31584":"YJ","31896":"ZN","31909":"ZY","31995":"XJ","32321":"PF","32327":"ZY","32418":"HG","32420":"XQ","32421":"HG","32438":"LG","32473":"GJ","32488":"TD","32521":"QJ","32527":"PB","32562":"ZSQ","32564":"JZ","32735":"ZD","32793":"PB","33071":"PF","33098":"XL","33100":"YA","33152":"PB","33261":"CX","33324":"BP","33333":"TD","33406":"YA","33426":"WM","33432":"PB","33445":"JG","33486":"ZN","33493":"TS","33507":"QJ","33540":"QJ","33544":"ZC","33564":"XQ","33617":"YT","33632":"QJ","33636":"XH","33637":"YX","33694":"WG","33705":"PF","33728":"YW","33882":"SR","34067":"WM","34074":"YW","34121":"QJ","34255":"ZC","34259":"XL","34425":"JH","34430":"XH","34485":"KH","34503":"YS","34532":"HG","34552":"XS","34558":"YE","34593":"ZL","34660":"YQ","34892":"XH","34928":"SC","34999":"QJ","35048":"PB","35059":"SC","35098":"ZC","35203":"TQ","35265":"JX","35299":"JX","35782":"SZ","35828":"YS","35830":"E","35843":"TD","35895":"YG","35977":"MH","36158":"JG","36228":"QJ","36426":"XQ","36466":"DC","36710":"JC","36711":"ZYG","36767":"PB","36866":"SK","36951":"YW","37034":"YX","37063":"XH","37218":"ZC","37325":"ZC","38063":"PB","38079":"TD","38085":"QY","38107":"DC","38116":"TD","38123":"YD","38224":"HG","38241":"XTC","38271":"ZC","38415":"YE","38426":"KH","38461":"YD","38463":"AE","38466":"PB","38477":"XJ","38518":"YT","38551":"WK","38585":"ZC","38704":"XS","38739":"LJ","38761":"GJ","38808":"SQ","39048":"JG","39049":"XJ","39052":"HG","39076":"CZ","39271":"XT","39534":"TD","39552":"TD","39584":"PB","39647":"SB","39730":"LG","39748":"TPB","40109":"ZQ","40479":"ND","40516":"HG","40536":"HG","40583":"QJ","40765":"YQ","40784":"QJ","40840":"YK","40863":"QJG"};
        /**
         * 获取当前字符的拼音首字母
         * @param ch {string} 字符串
         * @returns {*}
         */
        var checkCh=function(ch){
            var uni = ch.charCodeAt(0);
            if(uni > 40869 || uni < 19968) return ch;// 不在汉字范围内,返回原字符
            return (oMultiDiff[uni] ? oMultiDiff[uni] : strChineseFirstPY.charAt(uni-19968));// 多音字处理,否则在 strChineseFirstPY 字符串中找对应的首字母
        };
        /**
         * @param arr 逐个字符处理的结果数组
         * @returns {string[]} 返回所有可能的拼音首字母串数组
         */
        var mkRslt=function(arr){
            var arrRslt = [''];
            for(var i=0,len=arr.length;i<len;i++){
                var str = arr[i];
                var strlen = str.length;
                if(strlen == 1){
                    for(var k=0;k<arrRslt.length;k++){
                        arrRslt[k] += str;
                    }
                }else{
                    var tmpArr = arrRslt.slice(0);
                    arrRslt = [];
                    for(k=0;k<strlen;k++){
                        var tmp = tmpArr.slice(0);// 复制一个相同的arrRslt
                        //把当前字符str[k]添加到每个元素末尾
                        for(var j=0;j<tmp.length;j++){
                            tmp[j] += str.charAt(k);
                        }
                        //把复制并修改后的数组连接到arrRslt上
                        arrRslt = arrRslt.concat(tmp);
                    }
                }
            }
            return arrRslt;
        };
        var arrResult = []; //保存中间结果的数组
        for(var i=0,len=zhStr.length;i<len;i++){
            var ch = zhStr.charAt(i);//获得unicode码
            // 检查该unicode码是否在处理范围之内,在则返回该码对映汉字的拼音首字母,不在则调用其它函数处理
            arrResult.push(checkCh(ch));
        }
        //处理arrResult,返回所有可能的拼音首字母串数组
        return mkRslt(arrResult);
    };
    /**
     * 初始化关键字过滤元素
     * @param _opts
     *   - opts.fields {string|Array} 要过滤的字段的属性名
     *   - opts.pySupport {bool} 是否支持拼音首字过滤
     *   - opts.data {Array=} 要进行过滤的数据源
     *   - opts.afterFilter {function(newData,keyword)=} 过滤完成后的回调方法，newData:过滤后的数据；keyword:过滤关键字
     * @returns {object} obj
     *   - obj.filter(val) 手动过滤，val {string} 关键字
     *   - obj.setData(data,keyword) 重置过滤源数据，data {Array} 过滤数据源，keyword {string} 默认关键字
     *   - {Array} obj.getFilterData() 获取当前关键字过滤后的数据
     */
    $.fn.initFilter=function(_opts){
        var el=$(this);
        var opts=$.extend({
            fields:'name'
            ,pySupport:true// 拼音首字支持
//            ,data:[]// 要搜索的数据源
//            ,afterFilter:function(newData,keyword)// 过滤完成后的回调函数，newData:过滤后的数据
        },_opts);
        var _returnObj={};
        var _data// 过滤数据源
            ,_filterData=[]// 根据数据源生成的过滤字段字典
            ,keyword=''// 过滤关键字
            ,filterData;// 过滤后的数据
        el.val(keyword);
        var _destroy=function(){
            el.off('keyup.search');
        };
        // 根据关键字搜索数据源
        var _filter=function(val,doCb){
            if(!_data) return;// 没有数据源
            if(el.val()!=val) el.val(val);// 更新搜索框文本
            keyword = val.toLocaleUpperCase();
            if(keyword==''){// 关键字为空，返回全部
                filterData=_data;
            }else{
                filterData=[];
                for(var i= 0,vals;i<_data.length;i++){
                    vals=_filterData[i];
                    for(var j=0;j<vals.length;j++){
                        if(vals[j].toLocaleUpperCase().indexOf(keyword)!=-1){
                            filterData.push(_data[i]);
                            break;
                        }
                    }
                }
            }
            doCb && opts.afterFilter && opts.afterFilter(filterData,keyword);
        };
        var _initFilterData=function(){
            if(!_data) return;
            _filterData.length=0;
            // 生成过滤字段字典
            var fields=opts.fields
                ,pySupport=opts.pySupport;
            var len=_data.length
                ,i,item,val,filterVals;
            if(Object.prototype.toString.call(fields) === '[object Array]'){
                for(i= 0;i<len;i++){
                    filterVals=[];
                    item=_data[i];
                    for(var f= 0;f<fields.length;f++){
                        val=item[fields[f]];
                        var vType=typeof val;
                        if(vType=='boolean'||vType=='string'||vType=='number'){
                            val=String(item[fields[f]]).toLocaleUpperCase();
                            filterVals.push(val);
                            if(pySupport && /[\u4e00-\u9fa5]+/.test(val)){
                                filterVals=filterVals.concat($.makePyArr(val));
                            }
                        }
                    }
                    _filterData.push(filterVals);
                }
            }else{
                for(i= 0;i<len;i++){
                    filterVals=[];
                    val=_data[i][fields].toLocaleUpperCase();
                    filterVals.push(val);
                    if(pySupport && /[\u4e00-\u9fa5]+/.test(val)){
                        filterVals=filterVals.concat($.makePyArr(val));
                    }
                    _filterData.push(filterVals);
                }
            }
        };
        var _setData=function(data,val,doCb){
            _data=data;
            filterData=_data;
            _initFilterData();
            _filter(val||'',doCb);
        };
        var _init=function(){
            _setData(opts.data);// 初始化时不自动过滤
            el.on('keyup.search',function(){
                var val=el.val();
                if(val!=keyword) _filter(val,true);
            });
        };
        _returnObj.filter=_filter;
        _returnObj.setData=_setData;// 更新数据源
        _returnObj.getData=function(){// 获取数据源
            return _data;
        };
        _returnObj.getFilterData=function(){// 获取当前关键字过滤后的数据
            return filterData;
        };
        _destroy();
        _init();
        return _returnObj;
    };
    /**
     * 自动滚动页面，移入输入框，避免被小键盘遮挡
     */
    $.fn.autoShowInput=function(){
        var focusElBottom,oldScrollTop
            ,contentEl=$(this);
        window.onorientationchange = window.onresize = function() {
            setTimeout(function(){
                if(focusElBottom==undefined) return;
                var contentBcr = contentEl[0].getBoundingClientRect();
                var contentBottom=contentBcr.top+contentBcr.height;
                if(contentBottom>focusElBottom) return;// 触发元素在视图内
                contentEl[0].scrollTop=oldScrollTop+(focusElBottom-contentBottom);
            },0);
        };
        contentEl.on('tap.view','input.txt,textarea.txt',function(){// focus qq不识别
            var bcr = $(this)[0].getBoundingClientRect();
            focusElBottom=bcr.top+bcr.height;// 记录聚焦元素的 bottom
            oldScrollTop=contentEl[0].scrollTop;
        });
        contentEl.on('blur','input[type="text"],textarea',function(){
            focusElBottom=oldScrollTop=undefined;
        });
    };
})(Zepto);